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