查询字符串是否相同的查询

时间:2015-03-22 11:42:16

标签: java c++ algorithm hashmap

如果以下条件适用于所有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查询的这个问题。

4 个答案:

答案 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,来自ab的相应字符AB都需要第一次发生与以前配对。使用java集合,检查这一点的一种方法是put(a, b)中的Map m:如果结果不等于nullb,则答案应为NO。如果所有相等的a被映射到相等b s,则仍需要检查是否将不同的a映射到不同的b s,如果values()是唯一的

Collection<Character> values = m.values();
return new HashSet(values).size() == values.size() ? YES : NO;

(或者,重复检查,以反转AB的角色,或者并行执行这些检查或......) 如果查询量证明预处理是合理的,那么保持数组bdAbdB的“向后距离”:前一个位置的最后一个字符返回的距离是多远? (如果完全没有,请使用N+1。)对于查询,pos2k,如果bdA[i + pos] != bdB[j + pos]且至少有一个低于pos {1}},答案应该是否定的。 (如果两者都等于或高于k,则两者都是范围内的第一次出现。)