参考:基本是看他的http://bbezxcy.iteye.com/blog/1418354
参考论文《后缀数组--处理字符串的有力工具》
题目大意:要你求出一个字符串的最长回文子串
解题思路:常规方法是枚举每个字符,每个字符两边扩展,比如第i个字符,分奇偶两种
str[m] .... str[i] ..... str[n] m到i的字符串与n到i的字符串匹配
str[m]......str[i-1]str[i].....str[n],m到i-1的字符串与n到i的字符串匹配这两种
算法复杂度为O(n^2)
转为利用后缀数组,把整个字符串反向接在字符串后面,处理后的长度为原来的两倍,2*len
当以i为轴的回文子串,i为原字符串的下标,当长度为奇数回文子串时,i与2*len - i - 1匹配
当为偶数回文子串时,i与2 * len - i匹配
这样就转换成为了求新字符串的两个后缀的最长公共前缀,为了避免后面拼接的反向串成为公共前缀,用一个未出现的字符串把原串和反向串隔开
然后利用后缀数组中的height和rank的特性,两个后缀的起始位置为i,j,i < j, 那么最长公共前缀为height[rank[i]+1],height[rank[i]+2].....height[rank[j]的最小值,可以转换用RMQ来求区间最小
下面的best[i][j]表示从第j个位置到j+ 2 ^i个位置的最小值
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
const int maxn = 2010;
char str[maxn], tstr[maxn];
int height[maxn], sa[maxn], r[maxn], wx[maxn], wy[maxn], c[maxn];
int Log[maxn];
int best[20][maxn];
inline bool cmp(int *s, int a, int b, int l);
void suffix(char *s, int n, int m);
void cal_height(char *s, int *sa, int n);
void initRMQ(int n);
int lcp(int a,int b);
int main()
{
int n;
Log[0] = -1;
for(int i = 1; i < maxn; i++)
Log[i] = (i & (i-1)) ? Log[i-1] : Log[i-1] + 1 ; //i是否是2的幂
scanf("%s", str);
int len = strlen(str);
for(int i = 0; i < len; i++)
str[len + i + 1] = str[len - i - 1];
str[len] = 126;
n = 2 * len + 1;
str[n] = 0;
suffix(str, n + 1, 128);
cal_height(str, sa, n);
initRMQ(n);
int start, ans = 1,tmp;
for(int i = 0; i < len; i++)
{
tmp = lcp(i,n-i-1); ///首先考虑回文长度是奇数的情况
if(tmp * 2 - 1 > ans)
{
ans = tmp * 2 - 1;
start = i - tmp + 1;
}
tmp = lcp(i, n - i); ///考虑回文长度是偶数的情况
if(tmp * 2 > ans)
{
ans = tmp * 2;
start = i - tmp;
}
}
if(ans == 1)
printf("%c\n",str[0]);
else
{
for(int i = start; i < start + ans; i++)
printf("%c", str[i]);
printf("\n");
}
return 0;
}
inline bool cmp(int *s, int a, int b, int l)
{
return s[a] == s[b] && s[a + l] == s[b + l];
}
void suffix(char *s, int n, int m)
{
int *x = wx, *y = wy, *t, index;
for(int i = 0; i < m; i++)
c[i] = 0;
for(int i = 0; i < n; i++)
c[x[i] = s[i]]++;
for(int i = 1; i < m; i++)
c[i] += c[i - 1];
for(int i = n - 1; i >= 0; i--)
sa[--c[s[i]]] = i;
for(int j = 1; j < n; j *= 2, m = index)
{
index = 0;
for(int i = n - j; i < n; i++)
y[index++] = i;
for(int i = 0; i < n; i++)
{
if(sa[i] >= j)
y[index++] = sa[i] - j;
}
for(int i = 0; i < m; i++)
c[i] = 0;
for(int i = 0; i < index; i++)
c[x[y[i]]]++;
for(int i = 1; i < m; i++)
c[i] += c[i - 1];
for(int i = index - 1; i >= 0; i--)
sa[--c[x[y[i]]]] = y[i];
t = x; x = y; y = t;
x[sa[0]] = r[sa[0]] = 0;
index = 1;
for(int i = 1; i < n; i++)
{
if(j * 2 < n)
x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? index - 1 : index++;
else
r[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? index - 1 : index++;
}
}
}
void cal_height(char *s, int *sa, int n)
{
int k = 0;
for(int i = 0; i < n; height[r[i++]] = k)
{
if(k != 0)
k--;
for(int j = sa[r[i] - 1]; s[i + k] == s[j + k]; k++);
}
}
void initRMQ(int n)
{//初始化RMQ
for(int i = 1; i <= n ; i ++)
best[0][i] = height[i];
for(int i = 1; i <= Log[n] ; i++)
{
int limit = n - (1 << i) + 1;
for(int j = 1; j <= limit; j ++)
best[i][j] = min(best[i-1][j] , best[i-1][j+(1 << i >> 1)]);
}
}
int lcp(int a,int b)
{//询问a,b后缀的最长公共前缀
a = r[a];
b = r[b];
if(a > b)
swap(a,b);
a++;
int t = Log[b - a + 1];
return min(best[t][a] , best[t][b - (1 << t) + 1]);
}