LeetCode 459. Repeated Substring
题目
Given a non-empty string check if it can be constructed by taking a substring of it and appending multiple copies of the substring together. You may assume the given string consists of lowercase English letters only and its length will not exceed 10000.
Example 1:
Input: "abab"
Output: True
Explanation: It's the substring "ab" twice.
Example 2:
Input: "aba"
Output: False
Example 3:
Input: "abcabcabcabc"
Output: True
Explanation: It's the substring "abc" four times. (And the substring "abcabc" twice.)
解析
遇到这个题目感觉到似曾相识,是的,数据结构中经典的字符串匹配算法:KMP算法。KMP算法中是由模式串对主串进行匹配,其时间复杂度为O(m+n)。对于KMP算法我个人的理解是它记住了上一次匹配的位置(回溯),即next数组。而使用暴力破解,则又从开始进行匹配,极大的降低了匹配的效率。
KMP算法next数组
对于next数组的求解,我个人的理解是通过上一次匹配的next值来判断下一次是否和上次next值位置的char是否相等,即
若next[k] = j,则
next[k+1] = str[k+1] == str[j] ? j + 1 : 一直向前匹配直至为0。
示例如下:
模式串str | A | B | A | B | A | A |
---|---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
next数组 | 0 | 0 | 1 | 2 | 3 | 1 |
next数组求解过程如下:
- 初始设定next[0] = 0;
- next[1]:
首先看next[0]的值为0,str[0]='A',它和str[1]='B'不等,向前回溯,发现0已经是第一个,则next[1]=next[0]=0; - next[2]:
next[1]=0,str[0]='A',str[2]='A',两者相等,则next[2]=next[1]+1=1; - next[3]:
next[2]=1,str[1]='B',str[3]='B',两者相等,则next[3]=next[2]+1=2; - next[4]:
next[3]=2,str[2]='A',str[4]='A',两者相等,则next[4]=next[3]+1=3; - next[5]:
next[4]=3,str[3]='B',str[5]='A',两者不等,回溯-->
next[3]=2,str[2]='A',str[5]='A',两者相等,则next[5]=next[2]=1。
求解结束。
从求next数组的过程来看,如果串中一直重复某一串,则长度len-next[len-1]为重复串的长度。这个结论可以用到该题的解法上。
在本题中,求串中是否含有重复的子串,其过程可以将next数组求解出来,然后再判断。例,假如上述模式串为ABABAB,则next数组中最后一个应该为4,所重复的子串为AB,即长度6-4=2,并且6%2=0。因此便实现了。
实现(C语言)
bool repeatedSubstringPattern(char* s) {
int len = (int)strlen(s);
int next[len + 1], j = 0, k = -1;
next[0] = -1;
while (j < len) {
if (k == -1 || s[j] == s[k])
next[++j] = ++k;
else
k = next[k];
}
return next[len] && next[len] % (len - next[len]) == 0;
}
程序中next数组多设置了一位是为了计算的对齐,next[0]=-1,从1开始进行计算。
这里刚开始时的if判断只会运行前面的k==-1部分,后面s[j]==s[k]是不会执行到的,程序从左至右进行判断时,有一个条件满足便会执行if。
最后的return的结果判断,首先要判断next[len]是否为0,如果为0,则说明最后一个字符没有任何匹配,返回false;如果不为0,则需要判断长度len是不是重复子串的倍数。
通过此题可以复习数据结构中模式匹配的算法,希望读者在阅读的过程中,可以在纸上模拟计算一下。这样写程序时更加得心应手。