如果以下条件适用于所有1 <= i, j <= N
,则给定2个相同长度的字符串A和B被认为是等效的:
(Ai != Aj) <=> (Bi != Bj)
(Ai = Aj) <=> (Bi = Bj)
其中S[i]
表示字符串S的第i个(基于1的索引)字符。
注意:如果字符串A和B是等价的,字符串B和C是等价的,那么字符串A和C也是等价的。
给出2个相同长度为N的字符串A和B.我们需要回答Q查询。每个查询由3个整数i,j,k组成,对于给定的查询,我们需要检查字符串A[ i, i + k - 1]
和B[ j, j + k - 1]
是否相等。
示例:允许A = "abbcd"
和B = "cccad"
,我们有2个查询:
查询1:i = 1,j = 1且k = 2,则答案为否
查询2:i = 3,j = 3且k = 3,则答案为是
以最有效的方式回答此查询的最佳方法是什么?我认为可以在开始时通过存储所有26个英文字母的位置然后进行二分搜索类型的方法来完成一些预处理。但在某些情况下失败了。那么如何解决给定字符串和Q查询的这个问题。
答案 0 :(得分:2)
想法:创建一个规范化函数,让我们通过在“规范化”字符串上进行常规字符串匹配来检查一对字符串的等价关系。
等价关系似乎基本上检查匹配字符串的simple substitution cipher是否存在。因此,对于规范化步骤,我们使用基于第一次出现的字母替换字母(Java中的代码):
String normalize(String s) {
char available = 'A';
Map<Character, Character> seen = new HashMap<Character, Character>();
StringBuilder result = new StringBuilder();
for (int i = 0; i < s.length; s++) {
char c = s.charAt(i);
Character replacement = seen.get(c);
if (replacement == null) {
replacement = available++;
seen.put(c, replacement);
}
result.append(replacement);
}
return result.toString();
}
在查询中使用规范化:
boolean query(String a, String b, int i, int j, int k) {
return normalize(a.substring(i - 1, i + k)).equals(
normalize(b.substring(j - 1, j + k)));
}
现在我们可以将它集成到一个专用函数中,避免所有复制:
boolean query(String a, String b, int i, int j, int k) {
Map<Character, Character> seenA = new HashMap<Character, Character>();
Map<Character, Character> seenB = new HashMap<Character, Character>();
char available = 'A';
for (int p = 0; p < k; p++) {
char ca = a.charAt(i + p - 1);
char cb = b.charAt(j + p - 1);
Character replacementA = seenA.get(ca);
Character replacementB = seenB.get(cb);
if (replacementA == null ? replacementB != null :
!replacementA.equals(replacementB)) {
return false;
}
if (replacementA == null) {
seenA.put(ca, available);
seenB.put(cb, available);
available++;
}
}
return true;
}
答案 1 :(得分:1)
让A,B,你的2个长度为N的字符串。
让A2,B2,2矩阵N x N,其中A2(i,j)= A [i] == A [j]。
所以:
for 1 <= i,j <= N
{
A2(i,j) = A[i] == A[j]
B2(i,j) = B[i] == B[j]
}
然后比较2矩阵很长。但矩阵只包含{1,0}
。因此,您可以使用布尔值向量(在c ++中或在java / c#中列出)而不是考虑A2
矩阵。
所以让Va,Yb,两个布尔矢量(对于A&amp; B分别):
for 1 <= i,j <= N
{
Va(i * N + j) = A[i] == A[j]
Vb(i * N + j) = B[i] == B[j]
}
然后,您的财产相当于Va == Vb。
就速度而言,您可以改进实施,从而减少内存使用量。 64位整数车存储64个值。
所以,再次:
因此,让V64_a,Y64_b,两个长度为(N * N / 64)+1的64位无符号整数(对于A&amp; B分别为)的向量: 用0开始它们。
for(i=1 ; i< N ; i++)
{
// special case, comparing ith char with ith char : always true
Va[i*i/64] = Va[i*i/64] | (1 << i%64)
Vb[i*i/64] = Vb[i*i/64] | (1 << i%64)
for( j=i+1 ; j < N ; ++j)
{
Va[i*i/64] = Va[i*i/64] | ((A[i] == A[j] ? 1 : 0 ) << i%64)
Vb[i*i/64] = Vb[i*i/64] | ((B[i] == B[j] ? 1 : 0 ) << i%64)
}
}
然后,A == B&lt; =&gt; Va == Vb。
答案 2 :(得分:0)
好的,这里是完全有效的Java示例,每个查询都有复杂度为O(k),无需任何预处理。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JavaApplication14 {
public static void main(String[] args) {
String a = "abbcd";
String b = "cccad";
equivalency(a, b, 3, 3, 3);
equivalency(a, b, 1, 1, 2);
}
public static void equivalency(String a, String b, int i, int j, int k){
List<Character> orderA = new ArrayList<>();
List<Character> orderB = new ArrayList<>();
Map<Character, List<Integer>> mapA = createMap(a, i, k, orderA);
Map<Character, List<Integer>> mapB = createMap(b, i, k, orderB);
if (orderA.size() != orderB.size()) {
System.out.println("NO");
return;
}
for (int l = 0; l < orderA.size(); l++) {
List<Integer> valuesA = mapA.get(orderA.get(l));
List<Integer> valuesB = mapB.get(orderB.get(l));
if (valuesA.size() != valuesB.size()) {
System.out.println("NO");
return;
}
for (int m = 0; m < valuesA.size(); m++) {
if (valuesA.get(m).equals(valuesB.get(m)) == false) {
System.out.println("NO");
return;
}
}
}
System.out.println("YES");
}
public static Map<Character, List<Integer>> createMap(String input, int pos, int count, List<Character> characterOrder) {
Map<Character, List<Integer>> map = new HashMap<>();
input = input.substring(pos-1, pos+count-1);
for (int i = 0; i < input.length(); i++) {
char ch = input.charAt(i);
if (map.containsKey(ch) == false) {
characterOrder.add(ch);
map.put(ch, new ArrayList<>());
}
map.get(ch).add(pos);
}
return map;
}
}
此示例的输出为:
YES
NO
(如果您发现的输入应该具有与此程序实际不同的输出,请告诉我,我会修复它)
它实际上做了什么?当您查看比较的两个字符串中的第一个字符时,相同字符(对于每个字符串)的位置必须出现在与另一个字符串相同的位置。如果没有,则不相等。并且它与任何其他角色相同 - 第二个角色必须在两个字符串等中具有相同的外观位置列表。
此代码为每个角色创建外观列表并将其保留在此结构中:Map<Character, List<Integer>>
,您还需要知道首先找到哪个角色,比较这些位置的哪一个等等,所以对此我是使用List<Character>
。
在方法结束时,我在字符串A中首先发现了一个字符,在地图中找到它返回它的外观列表。我对String B做了同样的事情。然后我比较了字符串A和字符串B的所有值。如果它适合,我继续下一个字符,如果没有,它就不一样了。
答案 3 :(得分:0)
对于从1到pos
的每个字符位置k
,来自a
和b
的相应字符A
和B
都需要第一次发生或与以前配对。使用java集合,检查这一点的一种方法是put(a, b)
中的Map m
:如果结果不等于null
或b
,则答案应为NO。如果所有相等的a
被映射到相等b
s,则仍需要检查是否将不同的a
映射到不同的b
s,如果values()
是唯一的
Collection<Character> values = m.values();
return new HashSet(values).size() == values.size() ? YES : NO;
(或者,重复检查,以反转A
和B
的角色,或者并行执行这些检查或......)
如果查询量证明预处理是合理的,那么保持数组bdA
和bdB
的“向后距离”:前一个位置的最后一个字符返回的距离是多远? (如果完全没有,请使用N+1
。)对于查询,pos
从2
到k
,如果bdA[i + pos] != bdB[j + pos]
且至少有一个低于pos
{1}},答案应该是否定的。 (如果两者都等于或高于k
,则两者都是范围内的第一次出现。)