1、KMP算法
设有两串字符,第一串为主串,第二串为副串,求副串在主串的匹配index头。
主要是求next数组,感性认识是副串的前后缀匹配程度:
- "A"的前缀和后缀都为空集,共有元素的长度为0;
- "AB"的前缀为[A],后缀为[B],共有元素的长度为0;
- "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;
- "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;
- "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;
- "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;
- "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。
class Solution {
public:
int strStr(string haystack, string needle) { int m = haystack.length(), n = needle.length(); if (!n) { return 0; } vector<int> lps = kmpProcess(needle); for (int i = 0, j = 0; i < m; ) { if (haystack[i] == needle[j]) { i++; j++; } if (j == n) { return i - j; } if ((i < m) && (haystack[i] != needle[j])) { if (j) { j = lps[j - 1]; } else { i++; } } } return -1; } private: vector<int> kmpProcess(string& needle) { int n = needle.length(); vector<int> lps(n, 0); for (int i = 1, len = 0; i < n; ) { if (needle[i] == needle[len]) { lps[i++] = ++len; } else if (len) { len = lps[len - 1]; } else { lps[i++] = 0; } } return lps; } };
class Solution { public: vector<int> GetNext(string P) { vector<int> next(P.size(), 0); int p_len = P.size(); int i = 0; // P 的下标 int j = -1; next[0] = -1; while (i < p_len - 1) { if (j == -1 || P[i] == P[j]) { i++; j++; next[i] = j; } else j = next[j]; } vector<int> v2(next.size(), 0); copy(next.begin(), next.end(), v2.begin()); return v2; } //在 S 中找到 P 第一次出现的位置 / int strStr(string S, string P) { int i = 0; // S 的下标 int j = 0; // P 的下标 int s_len = S.size(); int p_len = P.size(); if (!p_len) return 0; vector<int> next = GetNext(P); while (i < s_len && j < p_len) { if (j == -1 || S[i] == P[j]) // P 的第一个字符不匹配或 S[i] == P[j] { i++; j++; } else j = next[j]; // 当前字符匹配失败,进行跳转 } if (j == p_len) // 匹配成功 return i - j; return -1; } };
//优化版 /* P 为模式串,下标从 0 开始 */ void GetNextval(string P, int nextval[]) { int p_len = P.size(); int i = 0; // P 的下标 int j = -1; nextval[0] = -1; while (i < p_len - 1) { if (j == -1 || P[i] == P[j]) { i++; j++; if (P[i] != P[j]) nextval[i] = j; else nextval[i] = nextval[j]; // 既然相同就继续往前找真前缀 } else j = nextval[j]; } }