【问题标题】:How to find all cyclic shifted strings in a given input?如何找到给定输入中的所有循环移位字符串?
【发布时间】:2012-01-15 15:08:57
【问题描述】:

这是一个编码练习。假设我必须确定一个字符串是否由另一个字符串的 循环移位 创建。例如:cababc 的循环移位,但cba 不是。

给定两个字符串s1s2,我们可以这样做:

如果 (s1.length != s2.length) 返回假 for(int i = 0; i

现在,如果我有一个字符串数组并且想要查找所有相互循环移位的字符串怎么办?例如:["abc", "xyz", "yzx", "cab", "xxx"] -> ["abc", "cab"], ["xyz", "yzx"], ["xxx"]

看来我必须检查字符串的所有对。有没有“更好”(更有效)的方法来做到这一点?

【问题讨论】:

    标签: string algorithm language-agnostic


    【解决方案1】:

    首先,您可以通过一次调用 contains() 来了解字符串 s1 是否是字符串 s2 的旋转,如下所示:

    public boolean isRotation(String s1, String s2){
        String s2twice = s2+s2;
        return s2twice.contains(s1);
    }
    

    也就是说,如果 s1 是“rotation”并且 s2 是“otationr”,则 concat 会给出“otationrotationr”,其中确实包含 s1。

    现在,即使我们假设这是线性的,或者接近它(例如,使用 Rabin-Karp 并非不可能),您仍然需要 O(n^2) 对比较,这可能太多了.

    您可以做的是构建一个哈希表,其中排序的单词是键,并且发布列表包含列表中的所有单词,如果已排序,则给出键(即 key("bca") 和 key( "cab") 都应该返回 "abc"):

    private Map<String, List<String>> index;
        /* ... */
    public void buildIndex(String[] words){
        for(String word : words){
            String sortedWord = sortWord(word);
            if(!index.containsKey(sortedWord)){
                index.put(sortedWord, new ArrayList<String>());
            }
            index.get(sortedWord).add(word);
        }
    }
    

    CAVEAT:对于每个键,哈希表将包含所有具有完全相同字母出现相同次数的单词(不仅仅是轮换,即“abba”和“baba”将具有相同的键,但isRotation("abba", "baba") 将返回 false)。

    但是一旦你建立了这个索引,你可以显着减少你需要考虑的对的数量:如果你想要 "bca" 的所有旋转,你只需要 sort("bca"),在哈希表,并检查(如果需要,使用上面的 isRotation 方法)发布列表中的单词是否是轮换的结果。

    【讨论】:

    • 他的问题是language-agnostic
    • 我在 Java 中提供了 sn-ps 只是为了举例...我使用了哈希表和字符串,我会说解决方案也与语言无关,不是吗?
    【解决方案2】:

    如果字符串与列表中的字符串数量相比较短,则可以通过将所有字符串旋转为某种正常形式(例如字典最小)来做得更好。然后按字典顺序排序并找到相同字符串的运行。那是O(n log n),我认为......忽略字符串长度。也许可以尝试一下。

    【讨论】:

      【解决方案3】:

      关于在表中查找对的方法,可能有很多更好的方法,但我首先想到的是对表进行排序并针对相邻的对应用检查。

      这比检查表中的每个字符串都更好更简单

      【讨论】:

        【解决方案4】:

        考虑为您希望测试的每个字符串构建一个自动机。

        每个自动机应该为字符串中每个可能的字符有一个入口点,每个字符都有一个转换,加上从结尾到开始的额外转换。

        如果您合并自动机,您可以进一步提高性能。

        【讨论】:

          【解决方案5】:

          我认为将 Patrick87 和 savinos 的答案结合起来会很有意义。具体来说,在 Java 风格的伪代码中:

          List<String> inputs = ["abc", "xyz", "yzx", "cab", "xxx"];
          Map<String,List<String>> uniques = new Map<String,List<String>>();
          for(String value : inputs) {
              String normalized = normalize(value);
              if(!uniques.contains(normalized)) {
                  unqiues.put(normalized, new List<String>());
              }
              uniques.get(normalized).add(value);
          }
          // you now have a Map of normalized strings to every string in the input
          // that is "equal to" that normalized version
          

          如 Patrick87 所述,规范化字符串可能最好通过选择导致最低字典顺序的字符串旋转来完成。

          值得注意的是,“最佳”算法可能在很大程度上依赖于输入……字符串的数量、字符串的长度、重复的数量等等。

          【讨论】:

            【解决方案6】:

            您可以在 O(s) 时间内使用 Booth 算法 (https://en.wikipedia.org/wiki/Lexicographically_minimal_string_rotation) 将所有字符串旋转为标准化形式,其中 s 是字符串的长度。

            然后,您可以将规范化形式用作 HashMap 中的键(其中值是输入中看到的一组旋转)。您可以一次通过数据填充此 HashMap。即,对于每个字符串

            • 计算归一化形式
            • 检查 HashMap 是否包含作为键的规范化形式 - 如果没有,则在此键处插入空 Set
            • 将字符串添加到HashMap中的Set中

            然后您只需要输出 HashMap 的值。这使得算法的总运行时间为 O(n * s) - 其中 n 是字数,s 是平均字长。总的空间使用量也是 O(n * s)。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-01-28
              • 1970-01-01
              • 2018-12-15
              • 2013-09-11
              • 2011-02-04
              相关资源
              最近更新 更多