【刷题日记】字符串-重复的子字符串-L459-Easy
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
思路
- 多次循环
KMP
算法,重点在于如何取子字符串作为模式串进行匹配,减少时间开销。
- 判断字符串 s 是否由重复子串组成,只要两个 s 拼接在一起,里面还出现一个 s 的话,就说明是由重复子串组成。在判断 s + s 拼接的字符串里是否出现一个 s 的的时候,要刨除 s + s 的首字符和尾字符,这样避免在 s + s 中搜索出原来的 s,我们要搜索的是中间拼接出来的 s。
- 数学推理:利用 next 数组
学习点
- 如何取子字符串?子字符串的长度一定是母串的因数,这样可以剪枝处理,节省很多循环。
代码
我的(复杂了):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| class Solution { public: void calNext(int *next, string &pattern) { int j = -1; next[0] = -1; for (int i = 1; i < pattern.length(); i++) { while (j != -1 && pattern[i] != pattern[j + 1]) { j = next[j]; } if (pattern[i] == pattern[j + 1]) { j += 1; } next[i] = j; } }
bool KMP(string &text, string &pattern, int *next) { int j = -1; for (int i = 0; i < text.length(); i++) { while (j != -1 && text[i] != pattern[j + 1]) { j = next[j]; } if (text[i] == pattern[j + 1]) { j++; } if (j == pattern.length() - 1) { return true; } } return false; }
bool repeatedSubstringPattern(string s) { int len = s.length(); bool flag = false; for (int i = len / 2; i >= 1; i--) { if (len % i != 0) continue; string pattern = s.substr(0, i); int next[pattern.length()]; calNext(next, pattern); int end = i; while (end != len) { string text = s.substr(end, i); if (!KMP(text, pattern, next)) break; else end += i; } if (end == len) return true; else continue; } return false; } };
|
官解的 KMP
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Solution { public: bool kmp(const string& query, const string& pattern) { int n = query.size(); int m = pattern.size(); vector<int> fail(m, -1); for (int i = 1; i < m; ++i) { int j = fail[i - 1]; while (j != -1 && pattern[j + 1] != pattern[i]) { j = fail[j]; } if (pattern[j + 1] == pattern[i]) { fail[i] = j + 1; } } int match = -1; for (int i = 1; i < n - 1; ++i) { while (match != -1 && pattern[match + 1] != query[i]) { match = fail[match]; } if (pattern[match + 1] == query[i]) { ++match; if (match == m - 1) { return true; } } } return false; }
bool repeatedSubstringPattern(string s) { return kmp(s + s, s); } };
|
利用 next 数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Solution { public: void getNext (int* next, const string& s){ next[0] = -1; int j = -1; for(int i = 1;i < s.size(); i++){ while(j >= 0 && s[i] != s[j + 1]) { j = next[j]; } if(s[i] == s[j + 1]) { j++; } next[i] = j; } } bool repeatedSubstringPattern (string s) { if (s.size() == 0) { return false; } int next[s.size()]; getNext(next, s); int len = s.size(); if (next[len - 1] != -1 && len % (len - (next[len - 1] + 1)) == 0) { return true; } return false; } };
|