zoukankan      html  css  js  c++  java
  • 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; } };
  • 相关阅读:
    【Java学习笔记】<集合框架>Iterator的子接口ListIterator
    【Java学习笔记】<集合框架>List特有的取出方式之一
    【Java学习笔记】集合框架Ⅱ
    【Java学习笔记】集合框架Ⅰ
    【PS】Ⅱ图像合成与渐变工具笔记
    【PS】Ⅰ基础及选框工具笔记
    [PS]简单的智能电视制作案例
    Spring中线程池的使用
    SpringBoot 多线程
    solr DIH 设置定时索引
  • 原文地址:https://www.cnblogs.com/gongkiro/p/13606289.html
Copyright © 2011-2022 走看看