28. 实现 strStr() 214. 最短回文串

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

 

posted @ 2020-09-03 10:46  GongKiro  阅读(178)  评论(0编辑  收藏  举报