【问题标题】:How to check if string has repeating pattern?如何检查字符串是否有重复模式?
【发布时间】:2019-09-14 10:08:06
【问题描述】:

我最近在一个面试问题中被问到这个问题:

给定一个输入字符串,检查它是否有重复模式并返回真或假。例如: "abbaabbaabbaabba""abba" 的重复模式

private boolean checkPattern(String input) {

}

我们如何使用正则表达式和不使用正则表达式来解决它?我对使用正则表达式和不使用正则表达式的方法都感兴趣。

【问题讨论】:

  • 试试这样的,1个或多个的重复模式,包括开始和结束字符^(abba)+$
  • 在问题中,它可以是任何字符串,而不仅仅是abba,因此正则表达式需要适用于所有场景。
  • 字符串必须由 only 重复模式组成吗? (即在字符串中添加“x”将使其不再具有 'abba' 的重复模式)
  • 是的,这是正确的@thebjorn
  • 你可以将字符串分成两等长,直到结果长度不是偶数,或者两半不相等......

标签: java regex algorithm


【解决方案1】:

我找到了一个使用正则表达式的解决方案。

诀窍是在非空的第一组上使用反向引用。

^(.+)(?:\1)+$

正如@PatrickParker 指出的那样,如果您需要最小的重复模式,那么您可以使用惰性限定符

^(.+?)(?:\1)+$

【讨论】:

  • 不过,他想要最短的重复匹配。所以他想要“abba”而不是“abbaabba”。您需要在第一组中添加一个惰性限定符,即^(.+?)(?:\1)+$
  • @PatrickParker 也许这是暗示的,。但这不是问题要求中的“给定输入字符串检查它是否具有重复模式并返回真或假”。所以无论是大的重复字符串还是短的重复字符串都应该足以满足真假问题。
【解决方案2】:

如果没有正则表达式,您将不得不遍历每个可能的子字符串,该子字符串的长度可以被原始字符串的长度整除,从索引 0 开始,在原始字符串中并检查它是否重复。要检查它是否重复,您只需检查字符串中的每个pattern.length() 字符数,看看它是否是模式。例如,它看起来像这样,

public boolean checkPattern(String str) {
    String pattern = "";
    for (int i = 0; i < str.length()/2; i++) {
        pattern += str.charAt(i);
        if (str.length() % pattern.length() == 0 && isRepeating(str, pattern)) {
            return true;
        }
    }
    return false;
}

public boolean isRepeating(String str, String pattern) {
    String leftover = str;
    int currIndex = leftover.indexOf(pattern);
    while (currIndex == 0) {
        if(currIndex + pattern.length() == leftover.length()) {
            return true; // you have reached the last possible instance of the pattern at this point
        }
        leftover = leftover.substring(currIndex + pattern.length());
        currIndex = leftover.indexOf(pattern);
    }
    return false;
}

就像用户 thebjorn 提到的那样,您可以防止对 isRepeating 的不必要调用,方法是仅在字符串长度可被模式长度整除时调用它,因此在 if 语句中进行模数检查。此外,模式在字符串中重复的最大长度是str.length()/2

【讨论】:

  • 输入字符串 "abbaabbaabbax" 仍然为真,但应该为假。
  • 对于长度为n 的子字符串,您可以通过首先检查字符串的长度是否可被n 整除,然后每个第n 个字符等于第一个字符来缩短检查子字符串中的字符。
  • @flash 我意识到一个字符串不能自我重复,所以我将条件从i &lt; str.length()更改为i &lt; str.length() - 1
  • @thebjorn 好点,我将编辑我的答案以仅检查原始字符串的长度可被整除的长度的可能子字符串
  • 最大模式长度为str.length() / 2
【解决方案3】:

我不知道 RegEx,所以我会以不同的方式来做。这仅适用于字符串不是部分重复字符串,即“xbcabbaabbaabbaxx”

首先,您获取输入字符串,并找到字符串大小的因素。素数意味着没有重复模式,因为重复模式意味着模式字符串长度的至少 2 的倍数。

感谢 Tot Zam:Finding factors of a given integer

public ArrayList<Integer> findFactors(int num) {        
    ArrayList<Integer> factors = new ArrayList<Integer>();

    // Skip two if the number is odd
    int incrementer = num % 2 == 0 ? 1 : 2;

    for (int i = 1; i <= Math.sqrt(num); i += incrementer) {

        // If there is no remainder, then the number is a factor.
        if (num % i == 0) {
            factors.add(i);

            // Skip duplicates
            if (i != num / i) {
                factors.add(num / i);
            }

        }
    }

    // Sort the list of factors
    Collections.sort(factors);

    return factors;
}

找到数字的因子后,在您的情况下为 16(结果为 1、2、4、8、16),并且不包括最大因子(它本身),您现在可以创建一个循环并迭代字符串的子字符串。您检查每个值与之前的值,并使用 continue 进行检查,直到获得正确的值

例如,粗略的草图:

boolean isRepeatingPattern = false;
for (Integer factor : factors) {
    int iterations = stringSize / factor;
    String previousSubstring = stringParam.substring(0, factor); 
    for (int i = 1; i < iterations; i++) {
        int index = i * factor;
        if (previousSubstring != stringParam.substring(index, index + factor)) break;
        if (i == iterations - 1) repeatingPattern = true;
    }
}

【讨论】:

    【解决方案4】:

    我意识到这篇文章有点过时了,但它出现在关于这个主题的谷歌搜索的顶部,并且由于没有一个答案提供我需要的东西,我最终制作了一个方法,我只是想要将其添加到此帖子中以供将来的搜索者使用。

    此方法生成找到的一个或多个模式以及每个模式在原始字符串中重复的次数。

    当我使用 string.matches() 尝试 @flakes 正则表达式时,只有当模式并排时它才匹配 true。所以它会匹配 101101 而不是 101234101(它似乎不知道模式 101 在那里出现了两次。

    所以,如果你只是想知道你的字符串是否并排有相同的模式,使用这个代码:

    if (myString.matches("^(.+?)(?:\\1)+$")) {
      //doSomethingHere
    }
    

    考虑到构建模式子串的想法,我想出了这个方法,它基本上构建了所​​有可能模式的列表。然后它遍历该列表并检查原始字符串以查看该模式是否在其中。显然它会忽略比较中的第一次命中,因为模式总是会在源字符串中命中一次……因为模式是从源字符串创建的。

    这是代码,显然您可以根据需要对其进行按摩:

    private void checkForPattern(String userString) {
        String               buildString;
        LinkedList<String>   patterns    = new LinkedList<>();
        int                  size        = userString.length();
        int                  hits;
        int                  newSize;
        String[]             coreString  = new String[size];
        Map<String, Integer> hitCountMap = new HashMap<>();
    
        for (int x = 0; x < size; x++) {
            coreString[x] = userString.substring(x, x + 1);
        }
    
        for (int index = 0; index < size - 1; index++) {
            buildString = coreString[index];
            for (int x = index + 1; x < size; x++) {
                buildString = buildString + coreString[x];
                patterns.add(buildString);
            }
        }
    
        for (String pattern : patterns) {
            String check = userString.replaceFirst(pattern, "");
            if (check.contains(pattern)) {
                newSize = userString.replaceAll(pattern, "").length();
                hits    = (size - newSize) / pattern.length();
                hitCountMap.put(pattern, hits);
            }
        }
    
        for (String pattern : hitCountMap.keySet()) {
            System.out.println("Pattern: " + pattern +
                               " repeated " + hitCountMap.get(pattern) +
                               " times.");
        }
    }
    

    【讨论】:

    • 在我看来patterns 将包含重复的元素。我建议检查buildString 是否不在列表中。
    • @circular 我对它进行了一些测试,效果很好……也许自己测试一下?
    【解决方案5】:

    您可以将子字符串放在另一个变量中,并为初始字符串运行一个循环,比较子字符串的第一个元素

    如果它匹配子字符串的运行条件。

    如果子字符串中任何前面的字符不匹配,则退出子字符串的 if 条件

    【讨论】:

      【解决方案6】:

      使用任意位置的所有子字符串创建Trie。在添加时,如果您最终将一个单词添加了两次,即之前添加了该单词,则表示它具有重复模式。

      如果您希望模式大于任何长度,请更改您的代码以仅存储大于该长度的单词。或者单个字符也可以是重复模式。

      【讨论】:

        【解决方案7】:

        您可以使用字符串拆分方法来获取重复模式。

        public static String getRepeatingPattern(String str) {
            String repeatingPattern =null;
            for(int i=0;i<str.length();i++) {
                repeatingPattern = str.substring(0, i+1);
                String[] ary = str.split(repeatingPattern);
                if(ary.length==0) {
                    break;
                }
            }
         return repeatingPattern;
        }
        

        【讨论】:

          【解决方案8】:
          private boolean checkPatternRepeatition(String s) {
              int secondMatch = (s + s).indexOf(s,1);
              return secondMatch < s.length();
          }
          

          只要字符串中存在模式重复,连接它们并搜索模式将导致索引小于字符串本身的长度。如果不是,它将返回字符串的长度。这需要 O(M^2) 时间复杂度,因为 indexOf() 时间复杂度是 O(M*N) 其中 M - 字符串的长度和 N - 模式的长度。

          【讨论】:

            猜你喜欢
            • 2023-03-31
            • 2018-09-25
            • 2018-08-21
            • 2017-02-01
            • 1970-01-01
            • 2012-04-21
            • 2011-11-27
            • 2020-09-20
            • 1970-01-01
            相关资源
            最近更新 更多