• LeetCode 214. Shortest Palindrome(回文串,回文树,KMP)


    https://leetcode.com/problems/shortest-palindrome/

    好题目啊!

    题目:

    Given a string s, you are allowed to convert it to a palindrome by adding characters in front of it. Find and return the shortest palindrome you can find by performing this transformation.

    Example 1:

    Input: "aacecaaa"
    Output: "aaacecaaa"
    Example 2:

    Input: "abcd"
    Output: "dcbabcd"

    题意:给你一个字符串,让你在这个字符串前面加入最少的字符,让整个字符串变成一个回文串。

    题解:回文串,让人第一联想到的是什么呢?我联想的第一个是动态规划。但是字符串长度超过5万,怕是要超时。接着我联想到了回文树。这是个解决回文串问题的神器。最后看题解,也可以用KMP解决。

    首先,根据题目的意思,我们找一个从左边第一个字符开始的最长回文串。首先用回文树怎么解决呢?简直是杀鸡用屠龙刀!

    输入一个字符串,建回文树。我们能计算那些问题呢?我轻轻列举一下,字符串中有多少个不同的回文串,字符串中回文串的总数,字符串中不同回文串的数量,字符串从某个字符开始的最长回文串长度,字符串从某个字符结束的最长回文串长度,字符串从某个字符开始的回文串个数,字符串从某个字符串结束的回文串个数,字符串中奇数回文串的数量,字符串中偶数回文串的数量............

    所以这道题目,用字符串从某个字符开始的最长回文串长度,可以解决。

    接下来简单介绍一个回文树:回文树是由两颗树组成,一棵树存储偶数串,一棵树存储奇数串,每个节点都是一种回文串。节点之间的边有两种,next[p][c] 表示节点p上的回文串两端加上c字符后形成的新回文串所在节点;fail[p] 表示节点p上的回文串的最长后缀回文串所在的节点。

    以上是整个树的架构。
    此外节点上还有别的值:该回文串的数量,该回文串后缀回文串的数量。

    回文树:

    #define MAX 50005
    
    struct Tree
            {
                    char s[MAX];
            int cnt[MAX];
            int num[MAX];
            int next[MAX][26];
            int fail[MAX];
            int len[MAX];
            int p;
            int last;
            int n;
    
            int newNode(int x)
            {
                memset(next[p],0,sizeof(next[p]));
                len[p]=x;
                cnt[p]=0;
                num[p]=0;
                return p++;
            }
    
            void init()
            {
                p=0;
                n=0;
                last=0;
                newNode(0);
                newNode(-1);
                fail[0]=1;
                fail[1]=0;
                s[0]=-1;
            }
    
            int GetFail(int x)
            {
                while(s[n-len[x]-1]!=s[n])
                    x=fail[x];
                return x;
            }
    
            void Add(int x)
            {
                x -= 'a';
                s[++n] = x;
                int cur = GetFail(last);
    
                if(!(last=next[cur][x]))
                {
                    int now = newNode(len[cur]+2);
    
                    fail[now]=next[GetFail(fail[cur])][x];
    
                    next[cur][x]= now;
    
                    num[now] = num[fail[now]]+1;
    
                    last = now;
                }
                cnt[last]++;
            }
    
            void Count()
            {
                for(int i=p-1;i>=0;i--)
                {
                    cnt[fail[i]]+=cnt[i];
                }
            }
    
    
            };
    
    class Solution {
    public:
        string shortestPalindrome(string s) {
            
            Tree tree;
            tree.init();
            for(int i=s.length()-1;i>=0;i--)
            {
                tree.Add(s[i]);
            }
            
            tree.Count();
            
            int len = tree.len[tree.last];
            
            string ans="";
            
            for(int i=s.length()-1;i>=len;i--)
            {
                ans+=s[i];
            }
            
            ans+=s;
            
            return ans;
            
        }
    };
    

    KMP

    这道题目还可以用KMP做,是不是感觉很厉害?
    这个思路最最最关键的地方,就是逆转思维!不知道逆转裁判你们有没有玩过。

    把字符串S 逆转成S1 ,然后把Str = S + "." +S1 拼接起来,这样我们要求的S左起最长回文串,就等于Str的最长公共前后缀!是不是要逆转思维,才能想到?

    最长公共前后缀是个啥?就是KMP,就是Next数组

    class Solution {
    public:
        int next[MAX*2];
        string shortestPalindrome(string s) {
            
            string s2="";
            for(int i=s.length()-1;i>=0;i--)
            {
                s2+=s[i];
            }
            
            string str=s+"."+s2;
            
            GetNext(str);
            
            int len = next[str.length()-1];
            
            string ans="";
            
            for(int i=s.length()-1;i>=len;i--)
            {
                ans+=s[i];
            }
            
            ans+=s;
            
            return ans;
            
        }
        
        void GetNext(string s)
        {
            next[0]=0;
            for(int i=1;i<s.length();i++)
            {
                int p = next[i-1];
                while(p>0&&s[i]!=s[p])
                {
                     p=next[p-1];
                }
                
                if(s[i]==s[p])
                {
                    next[i]=p+1;
                }
                else
                    next[i]=0;
            }
        }
        
    };
    
  • 相关阅读:
    13、文件修改及函数的基本使用
    12、文件处理 b模式
    作业3月16号
    作业3月13号
    11、文件处理 t模式
    10、数据类型内置之集合
    作业3月11号
    9、基础类型之列表、元组、字典
    作业3月10号
    8、for循环以及数字类型和字符串类型的内置方法
  • 原文地址:https://www.cnblogs.com/dacc123/p/12292223.html
Copyright © 2020-2023  润新知