我想知道如果给出二进制序列,我们可以使用Huffman算法检查它是否与字符串匹配。
例如,如果我们使用字符串“abdcc”和几个二进制序列,我们可以计算哪一个是使用Huffman算法的“abdcc”的可能表示
答案 0 :(得分:2)
有趣的谜题。正如j_random_hacker在评论中所提到的,可以使用回溯搜索来完成此操作。对字符串的有效霍夫曼编码有一些约束,我们可以使用它来缩小搜索范围:
我们可以定义一个函数matchHuffmanString
,该函数匹配具有霍夫曼编码比特流的字符串,并将霍夫曼代码表作为全局状态的一部分。首先,代码表为空,我们调用matchHuffmanString
,传递字符串的开头和比特流的开头。
调用该函数时,它会检查流中是否有足够的位来匹配字符串,如果没有则返回。 (2)
如果字符串为空,那么如果比特流也为空,则匹配并输出代码表。如果流为空但比特流不是,则没有匹配,因此函数返回。 (3)
如果字符保留在字符串中,则读取第一个字符。该函数检查代码表中是否已存在该字符的条目,如果是,则必须在比特流中存在相同的代码。如果没有,则没有匹配,因此函数返回(4)。如果存在,则函数调用自身,转到下一个字符并经过比特流中的匹配代码。
如果没有匹配的字符代码,则考虑由1位到32位(任意限制)的每个可能长度n的代码表示的可能性。从比特流中读取n个比特并检查以查看这样的代码是否会根据规则(1)与任何现有代码冲突。如果不存在冲突,则将代码添加到代码表中,然后函数递归,移动到下一个字符并经过长度为n位的假定代码。返回后,通过从表中删除代码来回溯。
在C中的简单实现:
#include <stdio.h>
// Huffman table:
// a 01
// b 0001
// c 1
// d 0010
char* string = "abdcc";
// 01 0001 0010 1 1
// reverse bit order (MSB first) an add extra 0 for padding to stop getBits reading past the end of the array:
#define MESSAGE_LENGTH (12)
unsigned int message[] = {0b110100100010, 0};
// can handle messages of >32 bits, even though the above message is only 12 bits long
unsigned int getBits(int start, int n)
{
return ((message[start>>5] >> (start&31)) | (message[(start>>5)+1] << (32-(start&31)))) & ((1<<n)-1);
}
unsigned int codes[26];
int code_lengths[26];
int callCount = 0;
void outputCodes()
{
// output the codes:
int i, j;
for(i = 0; i < 26; i++)
{
if(code_lengths[i] != 0)
{
printf("%c ", i + 'a');
for(j = 0; j < code_lengths[i]; j++)
printf("%s", codes[i] & (1 << j) ? "1" : "0");
printf("\n");
}
}
}
void matchHuffmanString(char* s, int len, int startbit)
{
callCount++;
if(len > MESSAGE_LENGTH - startbit)
return; // not enough bits left to encode the rest of the message even at 1 bit per char (2)
if(len == 0) // no more characters to match
{
if(startbit == MESSAGE_LENGTH)
{
// (3) we exactly used up all the bits, this stream matches.
printf("match!\n\n");
outputCodes();
printf("\nCall count: %d\n", callCount);
}
return;
}
// read a character from the string (assume 'a' to 'z'):
int c = s[0] - 'a';
// is there already a code for this character?
if(code_lengths[c] != 0)
{
// check if the code in the bit stream matches:
int length = code_lengths[c];
if(startbit + length > MESSAGE_LENGTH)
return; // ran out of bits in stream, no match
unsigned int bits = getBits(startbit, length);
if(bits != codes[c])
return; // bits don't match (4)
matchHuffmanString(s + 1, len - 1, startbit + length);
}
else
{
// this character doesn't have a code yet, consider every possible length
int i, j;
for(i = 1; i < 32; i++)
{
// are there enough bits left for a code this long?
if(startbit + i > MESSAGE_LENGTH)
continue;
unsigned int bits = getBits(startbit, i);
// does this code conflict with an existing code?
for(j = 0; j < 26; j++)
{
if(code_lengths[j] != 0) // check existing codes only
{
// do the two codes match in the first i or code_lengths[j] bits, whichever is shorter?
int length = code_lengths[j] < i ? code_lengths[j] : i;
if((bits & ((1 << length)-1)) == (codes[j] & ((1 << length)-1)))
break; // there's a conflict (1)
}
}
if(j != 26)
continue; // there was a conflict
// add the new code to the codes array and recurse:
codes[c] = bits; code_lengths[c] = i;
matchHuffmanString(s + 1, len - 1, startbit + i);
code_lengths[c] = 0; // clear the code (backtracking)
}
}
}
int main(void) {
int i;
for(i = 0; i < 26; i++)
code_lengths[i] = 0;
matchHuffmanString(string, 5, 0);
return 0;
}
输出:
match!
a 01
b 0001
c 1
d 0010
Call count: 42
上面的代码可以通过遍历字符串来改进,只要它遇到已经有代码的字符,并且只有在找到它没有的字符时才重复。此外,它仅适用于没有空格的小写字母a-z,不进行任何验证。我必须对它进行测试以确定,但我认为这对于长字符串来说是一个容易处理的问题,因为任何可能的组合爆炸只会在遇到表中没有代码的新字符时发生,即使这样它也是主题约束。