如果以下条件适用于所有 1 <= i, j <= N
,则给定2个相同长度的字符串A和B被认为是等效的:
(Ai != Aj) <=> (Bi != Bj)
(Ai = Aj) <=> (Bi = Bj)
其中 S[i]
表示字符串S的第i个(基于1的索引)字符 .
NOTE : If strings A and B are equivalent, strings B and C are equivalent, then strings A and C are also equivalent.
给定2个相同长度为N的字符串A和B.我们需要回答Q查询 . 每个查询由3个整数i,j,k组成,对于给定的查询,我们需要检查字符串 A[ i, i + k - 1]
和 B[ j, j + k - 1]
是否相等 .
Example :设 A = "abbcd"
和 B = "cccad"
,我们有2个查询:
查询1:i = 1,j = 1且k = 2,则答案为否查询2:i = 3,j = 3且k = 3,则答案为是
什么是以最有效的方式回答此查询的最佳方式?我认为可以在开始时通过存储所有26个英文字母的位置然后进行二分搜索类型的方法来完成一些预处理 . 但在某些情况下失败了 . 那么如何解决给定字符串和Q查询的这个问题 .
4 回答
想法:创建一个规范化函数,让我们通过在“规范化”字符串上进行常规字符串匹配来检查一对字符串的等价关系 .
等价关系似乎基本上检查匹配字符串的simple substitution cipher是否存在 . 因此,对于规范化步骤,我们使用一个基于第一次出现的字母替换字母(Java中的代码):
在查询中使用规范化:
现在我们可以将它集成到一个专用函数中,避免所有复制:
让A,B,你的2个长度为N的字符串 .
令A2,B2,2矩阵N×N,其中A2(i,j)= A [i] == A [j] .
所以:
然后比较2矩阵是相当长的 . 但矩阵仅包含
{1,0}
. 因此,不要考虑A2
矩阵,而是可以使用布尔值的向量(在c或java / c#中的列表中) .让Va,Yb,两个布尔矢量(对于A和B分别):
然后,您的 property 相当于Va == Vb .
在速度方面,您可以改进实施,从而减少内存使用量 . 64位整数车存储64个值 .
所以,再次:
所以让V64_a,Y64_b,两个64位无符号向量(对于A和B分别)的长度为(N * N / 64)的向量1:用0开始它们 .
然后,A == B <=> Va == Vb .
好的,这里是完全工作的Java示例,复杂度为O(k),每个查询都没有任何预处理 .
此示例的输出是:
(如果你发现输入的输出应该比这个程序实际有的不同,请告诉我,我会修复它)
它实际上做了什么?当您查看比较的两个字符串中的第一个字符时,相同字符(对于每个字符串)的位置必须出现在与另一个字符串相同的位置 . 如果没有,则不相等 . 并且它与任何其他角色相同 - 第二个角色必须在两个字符串等中具有相同的外观位置列表 .
此代码为每个字符创建外观列表并将其保留在此结构中:
Map<Character, List<Integer>>
,您还需要知道首先找到哪个字符,哪个字符用于比较这些位置,所以为此我使用List<Character>
.在方法结束时,我首先在字符串A中发现字符,在 Map 中找到它返回其外观的结构 . 我对String B做了同样的事情 . 然后我比较了字符串A和字符串B的所有值 . 如果它适合,我继续下一个字符,如果没有,它就不一样了 .
对于从1到
k
的每个字符位置pos
,来自A
和B
的相应字符a
和b
都需要首次出现或者像以前一样进行配对 . 使用java Collections,检查这个的一种方法是在Map m
中put(a, b)
:如果结果既不等于null
也不等于b
,答案应该是NO . 如果所有相等的a
被映射到相等b
s,则仍然需要检查是否将不同的a
s映射到不同的b
,如果values()
是唯一的,那么它应该为真(或者,重复检查以反转
A
和B
的角色或并行执行这些检查或......)如果查询的数量证明预处理是合理的,那么保持数组
bdA
和bdB
为"backward distances":前一个位置的最后一个字符返回前后多远? (如果没有,请使用N+1
. )对于查询,pos
从2
到k
,如果bdA[i + pos] != bdB[j + pos]
且至少有一个低于pos
,答案应该是没有 . (如果两者都等于或高于k
,则两者都是范围内的第一次出现 . )