28.实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = "hello", needle = "ll"
输出: 2
示例 2:
输入: haystack = "aaaaa", needle = "bba"
输出: -1
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/implement-strstr
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
1.类双指针的做法
一个index指针指向haystack,一个j指针指向 needle ,遇到haystack[index+j] == needle[j]时,j++,不相等时,将j恢复0,index++,继续做对比
一直遍历到在haystack中找到needle的子字符串,返回true;或者打到临界条件i >haystack.size() - needle.size(),则返回false。
class Solution { public: int strStr(string haystack, string needle) { if (needle.empty() ) { return 0; } if (haystack.size() < needle.size()) { return -1; } int src_index(0), drc_index(0); int ret(0); while (src_index < haystack.size() - needle.size()+1) { if (haystack[src_index] == needle[0]) { int i = 1; for (; i < needle.size();++i) { if (haystack[src_index+i] != needle[i]) { break; } } if (i == needle.size()) { return src_index; } } src_index++; } return -1; } };
这种做法其实会造成大量的冗余计算,比如h字符串是BBC ABCDAB ABCDABCDABDE,n字符串是ABCDABD(这两个字符串其实是经典的KMP讲解),当匹配到
按照上方双指针的做法,n字符串下标j将会从0开始,而h字符串的index = index + 1,可是我们从上一个步骤中是知道了ABCDAB和ABCDABD前面的ABCDAB是匹配到的,
而n[0]和h[index+1]是不匹配的,而且可以看到n字符串的ABCDAB,拥有长度为2的相同前缀和后缀AB,既然这样,那刚刚匹配到的h子字符串后缀长度为2的子子字符串也就跟h
的前缀长度为2的子字符串是相等的,也即是:
那这样,是不是可以让h字符串和n字符串在同样的AB位置开始呢?
这当然是可以的,也就是常说的KMP算法,来自克努斯-莫里斯-普拉特算法
上面的解析中,可以看到个关键的字眼 可以看到n字符串的的子串ABCDAB,拥有长度为2的相同前缀和后缀AB,这也就是KMP中很关键的next数组,这个数据就记录的字符串k位置
之前,拥有的相同后缀前缀的长度,比如ABCDABD,在k=B(第二个B)的时候,拥有长度为1的相同前缀后缀A;在k=D(第二个D)的时候,拥有长度为2的相同前缀后缀AB,
那下面就来算n字符串的next数组了,很简单,就对比和回溯.
n: A B C D A B D
next: -1 0 0 0 0 1 2 0
std::vector<int> commputeNext(string pattern) { vector<int> next(pattern.size() + 1, 0); next[0] = -1; next[1] = 0; int indexNext = 2; int j = 0; //j表示pattern[index]位置 pattern[0,index]前缀和后缀相同的位置 while (indexNext < next.size ()){ if (pattern[indexNext - 1] == pattern[j]){ next[indexNext] = j + 1; j++; indexNext++; } else if (j == 0){ next[indexNext] = 0; indexNext++; } else{ j = next[j]; } } return next; }
至此,拿到了next数组后,在回头实现strstr就很简单了,依然是双指针,i指向h的位置,一直累加就行,而j指针在h[i] == n[j]的时候,j = j + 1;
而相等的时候j = next[j],拿到相同前缀后缀的数量,再从这之后开始算,在j == n的size时,则返回true;或者到了临界后返回false;
class Solution { public: std::vector<int> commputeNext(string pattern) { vector<int> next(pattern.size() + 1, 0); next[0] = -1; next[1] = 0; int indexNext = 2; int j = 0; //j表示pattern[index]位置 pattern[0,index]前缀和后缀相同的位置 while (indexNext < next.size ()){ if (pattern[indexNext - 1] == pattern[j]){ next[indexNext] = j + 1; j++; indexNext++; } else if (j == 0){ next[indexNext] = 0; indexNext++; } else{ j = next[j]; } } return next; } int strStr(string haystack, string needle) { if (needle.empty()) return 0; if (haystack.empty()) return -1; vector<int> next = commputeNext(needle); int j = 0; for (int i = 0; i < haystack.size();) { if (j == -1 || haystack[i] == needle[j]){ i++; j++; } else{ j = next[j]; } if (j == needle.size()) return i - j; } return -1; } };
214. 最短回文串
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
示例 1:
输入: "aacecaaa"
输出: "aaacecaaa"
示例 2:
输入: "abcd"
输出: "dcbabcd"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-palindrome
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
同样的KMP可以作用在LeetCode的最短回文串中
因为题目要求的是增加s字符串的前缀,让新的s成为回文串,abcd ---> dcbabcd
那怎么在这里用上KMP呢,直接算abcd的next数组?显然是不行的。我们知道next数组的关键是 可以看到n字符串的的子串ABCDAB,拥有长度为2的相同前缀和后缀AB
如果把s数组反转下变成rs dcba,那 rs+s 就是acbaabcd,很明显,这是个回文串,也很明显这不是最短的回文串,因为还有个更短的acbabcd,那就是说,我们只需要dcba前面的bcd,
再跟s组合起来就是最短的了,那怎么求出这个dcb呢?
so,组合格新的字符串news = s + “#” + rs,也就是abcd#dcba,那就来求news的next数组咯,这里再会议下next数组的意义。。。。。。。。。。
在求到最后一个的时候,得出来的值是不是就是news的相同前缀后缀的长度了??yes, KMP yes。这个长度肯定不会比rs或者说s的长度大,为什么呢?因为有个 # 在搞事情。
既然求出了news的的最后相同前缀后缀的长度了,那上面要~求(博大精深的汉子)的dbc怎么搞呢? 把rs后面的相同的部分搞掉不就行咯。
dcba搞掉个a,next[4]的值是1(只有a是相同的,ab可不等于ba),那结果就是rs[0,len(rs) - next[4]];
最后在新的rs后面加上s不就是最短回文串?
class Solution { public: string shortestPalindrome(string s) { if (s == "") { return ""; } string reverse = ""; for (int i = s.size() - 1; i >= 0; --i) { reverse += s[i]; } string pattern = s + '#' + reverse; vector<int>next(pattern.size() + 1, 0); next[0] = -1; next[1] = 0; // A没有前后缀 int i = 2; // i表示next数组的索引 int k = 0; while (i < next.size()) { if (pattern[i - 1] == pattern[k]) { // pattern索引比next索引小1 next[i] = k + 1; //k = next[i]; ++i;
++k; } else if (k == 0) { next[i] = 0; ++i; } else { k = next[k]; } } int max_len = next[next.size() - 1]; string ret = reverse.substr(0, reverse.size() - max_len) + s; return ret; } };