1

I was recently asked this in an interview question:

Given a input string check if it has repeating pattern and return true or false. For example: "abbaabbaabbaabba" is a repeating pattern of "abba"

private boolean checkPattern(String input) {

}

How can we solve it using regex and also without regex? I am interested in both the approaches with regex and without regex.

flash
  • 1,564
  • 3
  • 27
  • 87

7 Answers7

3

For what it's worth I found a solution using regex.

The trick is to use the back-reference on a non-empty first group.

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

And as @PatrickParker points out, if you need the smallest repeating pattern then you can use a lazy qualifier

^(.+?)(?:\1)+$
flakes
  • 12,841
  • 5
  • 28
  • 69
  • 1
    he wanted the shortest repeating match, though. so he wanted "abba" not "abbaabba". you need to add a lazy qualifier to the first group, i.e. `^(.+?)(?:\1)+$` – Patrick Parker Apr 30 '19 at 22:11
  • @PatrickParker Maybe that's implied,. but it wasn't the in the question requirements "Given a input string check if it has repeating pattern and return true or false". So either a large repeating string and short repeating string should be enough to satisfy the question for true or false. – flakes Apr 30 '19 at 22:16
2

Without regex, you would have to loop through every possible substring of a length that the original string's length is divisible by, starting from index 0, in the original string and check if it repeats. To check if it repeats, you simply just check every pattern.length() number of characters in the string to see if its the pattern or not. For example, it would look like this,

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;
}

Like user thebjorn mentioned, you can prevent unnecessary calls to isRepeating by only calling it when the string's length is divisble by the pattern's length, hence the modulus check in the if statement. Also, the max length a pattern can be for it to repeat in a string is str.length()/2.

Chris Gong
  • 7,336
  • 4
  • 26
  • 43
  • It still gives true for input string "abbaabbaabbax" but it should be false. – flash Apr 25 '19 at 03:53
  • for a substring of length `n` you can short-circuit the check by first checking that the length of the string is divisible by `n`, and then that each nth character is equal to the first character in the substring. – thebjorn Apr 25 '19 at 03:54
  • @flash I realized that a string can't repeat on itself, so I changed the condition from `i < str.length()` to `i < str.length() - 1` – Chris Gong Apr 25 '19 at 03:55
  • @thebjorn good point, I'll edit my answer to only check possible substrings of lengths that the length of the original string is divisble by – Chris Gong Apr 25 '19 at 03:56
  • the max pattern length would be `str.length() / 2` – thebjorn Apr 25 '19 at 04:03
1

I don't know RegEx so I will do it a different way. And this only applies if the String is not a partial repeating string i.e. "xbcabbaabbaabbaxx"

First, you take the input string, and find the factors of the string size. A prime number will mean that there are no repeating patterns, as a repeating pattern implies a multiple of at least 2 of the pattern String length.

Thanks to 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;
}

Once you find the factors of the number, in your case 16 (result being 1,2,4,8,16), and excluding the largest factor (which is itself), you can now create a loop and iterate on substrings of the string. You check for each value against its previous value, and check until you get a correct value using continue

For example, a rough sketch:

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;
    }
}
E. Ancuta
  • 51
  • 8
1

I realize this post is a little dated, but it came up at the top of a google search on this topic, and since none of the answers provided what I needed, I ended up making a method that did and I simply wanted to add it to this post for future searchers.

This method produces the pattern or patterns that were found and the number of times each pattern repeats in the original string.

When I tried @flakes regex using string.matches(), it only matched true if the patterns were side by side. So it would match 101101 but not 101234101 (it didn't seem to know that the pattern 101 was in there twice.

So, if you simply need to know if your string has the same pattern in it side by side, use this code:

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

Taking the idea of building a substring of patterns to the nth degree, I came up with this method, which basically builds a list of all possible patterns. Then it iterates through that list and checks the original string to see if that pattern is in it. Obviously it will ignore the first hit in the comparison since the pattern will always hit true one time in the source string ... on account of the pattern being created from the source string.

Here is the code, obviously you can massage it for your needs:

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.");
    }
}
Michael Sims
  • 1,622
  • 10
  • 20
  • Seems to me that `patterns` will contain duplicate elements. I suggest to check whether `buildString` is not already in the list. – circular May 15 '21 at 05:41
  • @circular I ran some tests on it, and it worked pretty good ... maybe test it yourself? – Michael Sims May 15 '21 at 06:08
0

You could take the substring in another variable and run a loop for the initial string comparing for the first element of the substring

If it matches run if condition for the substring.

If any of the preceeding characters in the substring do not match, exit the substring's if condition

0

Create Trie with all the sub strings at any location. While adding if you end up adding one word twice that is the word has been added previously, it means it has repeating pattern.

If you want pattern grater than any length, change your code to store only words grater than that length. Or a single character can also be a repeating pattern.

Akash Sharma
  • 632
  • 3
  • 6
0

You can use String split method to get the repeating pattern.

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;
}
prasadg
  • 539
  • 6
  • 10