3

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.

12
  • Try something like this, a repeating pattern of 1 or more including start and end characters ^(abba)+$ Commented Apr 25, 2019 at 3:39
  • In the problem, it can be any string not just abba so regex needs to work for all the scenarios. Commented Apr 25, 2019 at 3:41
  • 1
    Must the string consist of only the repeating pattern? (i.e. adding an 'x' to the string would make it not hava a repeating pattern of 'abba') Commented Apr 25, 2019 at 3:44
  • yes that's correct @thebjorn Commented Apr 25, 2019 at 3:45
  • you could split the string in 2 equal lengths until the resulting length wasn't an even number, or the two halves weren't equal... Commented Apr 25, 2019 at 3:49

9 Answers 9

4

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)+$
Sign up to request clarification or add additional context in comments.

2 Comments

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)+$
@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.
3

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.

5 Comments

It still gives true for input string "abbaabbaabbax" but it should be false.
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.
@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
@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
the max pattern length would be str.length() / 2
2
private boolean checkPatternRepeatition(String s) {
    int secondMatch = (s + s).indexOf(s,1);
    return secondMatch < s.length();
}

Whenever there is a pattern repetition present in the string, concatenating them and searching for the pattern will result in a index that is less than the length of the string itself. If not it will return the length of the string. This takes O(M^2) time complexity as the indexOf() time complexity is O(M*N) where M - Length of the string and N - Length of the pattern.

Comments

1

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.

Comments

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

Comments

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.");
    }
}

2 Comments

Seems to me that patterns will contain duplicate elements. I suggest to check whether buildString is not already in the list.
@circular I ran some tests on it, and it worked pretty good ... maybe test it yourself?
1

Use this one line solution

private boolean checkPattern(String input) {
    return ((input + input).indexOf(input, 1) != input.length());

}

If a string of length abcabc is constructed by repeating substring abc, then abcabcabcabc. So if we ignore the very first occurrence of the subtring abc which starts from index 0, then we can see that the resulting string starting from the second occurrence of the substring abc. However, if it is not constructed from the repeating substrings, the first re-occurence will only start from the beginning of second string.

Comments

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

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.