请考虑以下问题:
string s = "fffffssss"
编码的字符串将被5xf4xs
但是如果s
中有编码模式怎么办?例如s="5xfxxx"
,在编码器中我应该怎么做才能避免歧义?前提是编码字符串必须短于原始字符串。
要编码5xfxxx
,你会得到1x51xx1xf3xx
,它没有任何歧义(只有一种方法可以解码这样的字符串,你必须考虑三元组)。当您的字符串中连续有超过 10 个相似的字符时,事情可能会变得更加棘手,但仍然不会有任何歧义。
至于编码字符串必须短于原始字符串的约束,没有这样的保证。x
将被编码为长三倍的1xx
。这是您最坏的情况:结果比原始结果长 3 倍。
如果你正在寻找压缩字符串的方法,我建议你看看霍夫曼的编码,它简单而高效(它在压缩方面几乎是最佳的,并且以线性时间运行)。不过,您将不得不考虑二进制字符串。
如果您希望保持相同的编码方案,其中dxc
会导致c(字符)的d(数字)重复,那么您可以简单地使用5
1xx
y
对输入进行编码,例如5xy
。是的,每当您在输入中找到一个数字后跟一个x
时,您将支付 2 个额外字符的价格。
没有(非有损)编码可以保证输出始终短于输入。甚至更强:没有编码可以保证它将始终创建输入长度或更短的输出,除了根本不编码任何内容(这将始终产生等于其输入的输出长度)。所有压缩方案都依赖于输入中的冗余,并且根本不会使用真正的随机数据压缩任何内容。因此,压缩方案是否良好取决于它是否可以很好地利用其预期输入中的冗余。
保证您永远不会支付超过 1 个字符的罚款的简单方案是使用初始令牌来指示字符串是否已编码。例如,假设如果未执行编码,则0
第一个字符,如果编码,则1
。然后
encode("1x2x3x4x") = "01x2x3x4x"; // only 1 character longer than input
encode("1x2x3x4x") = "111xx21xx31xx41xx"; // not so good: 8 chars longer
我假设"aaa
一个想法是引入一个特殊的转义字符,例如哈希"#"。每当输入有一系列数字时,让编码算法在这样的序列之后附加一个哈希。这样,它永远不会与nxc模式混淆。在解码中,您将删除此类尾随哈希。
每当输入本身有一个哈希值时,然后以与上面相同的方式对其进行转义:在其后面附加一个额外的哈希值。
因此,在您的示例中,5xfxxx
将被编码为5#xf3xx
.但是,如果可以用nxc表示法写入一系列数字,则不会使用哈希。所以999x1
会被编码为3x91
,而122x1
会被编码为122#x1
。 类似地,###
将被编码为3x#
,而不是转义任何哈希。因此,应用nxc模式将始终优先于转义。
以下是这些编码/解码函数的一些JavaScript实现,严重依赖基于正则表达式的替换。你可以玩它:
function encode(s) {
// If a character occurs 3 or more times in sequence, encode that sequence;
// Otherwise, append a hash after any sequence of digits,
// and after each individual hash:
return s.replace(/(.)11+|d+|#/g, (m, ch) =>
ch ? m.length + "x" + ch : m + "#");
}
function decode(s) {
// If a nxc sequence is found, decode it
// Otherwise, if a character is followed by a hash, remove the hash
return s.replace(/(d+)x(.)|(.)#/g, (m, times, ch, esc) =>
times ? ch.repeat(+times) : esc);
}
// I/O management of this snippet:
let elemInput = document.querySelector("#input");
let elemEncoded = document.querySelector("#encoded");
let elemDecoded = document.querySelector("#decoded");
let elemCheck = document.querySelector("#check");
elemInput.addEventListener("input", function () { // Whenever input changes:
let encoded = encode(this.value); // Encode...
let decoded = decode(encoded); // ...and decode the encoded string again
elemEncoded.textContent = encoded;
elemDecoded.textContent = decoded;
// Check whether the decoded string is equal to the input:
elemCheck.textContent = this.value == decoded ? "OK" : "Difference!";
});
Input: <input id="input">
<div>Encoded: <span id="encoded"></span></div>
<div>Decoded: <span id="decoded"></span></div>
<div>Check: <span id="check"></span></div>
显然,这意味着某些输入将具有比原始输入更长的编码等效项。除非使用始终编码为与输入一样长的字符串的算法,或者除非输出可能包含永远不会出现在输入中的内容,否则无法防止输出长于输入的情况。
注意:我从正则表达式中删除了s
标志,因为并非所有浏览器都支持它,但如果输入中可以出现换行符,它应该在那里。