【后缀数组】 求字符串的最长回文串

本文详细阐述了一种高效算法,用于在给定字符串中查找最长回文子串。通过将原串与其反转串结合,并巧妙利用后缀数组、LCP等数据结构,该算法能在较短的时间内解决经典回文子串问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



求一个串的最长回文子串

把原串的反串加个原串后面,中间加个没有出现过的字符

然后,原串中,下标i在反串中对应的位置为2*l-i

如果求以i为对称轴的回文串,我们求suffix(i)和suffix(2*l-i)的LCP

如果求以i和i+1为对称轴的回文串,我们求suffix(i+1)和suffix(2*l-i)的LCP


代码:

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=2010;

char s[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
int n;

void build_sa(int n, int m)
{
    int i,*x=t,*y=t2;
    //基数排序
    for(i=0;i<m;i++) c[i]=0;
    for(i=0;i<n;i++) c[x[i]=s[i]]++;
    for(i=1;i<m;i++) c[i]+=c[i-1];
    for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k<<=1)
    {
        int p=0;
        //直接利用sa数组排序第二关键字
        for(i=n-k;i<n;i++) y[p++]=i;
        for(i=0;i<n;i++) 
            if(sa[i]>=k)  y[p++]=sa[i]-k;
        for(i=0;i<m;i++) c[i]=0;
        for(i=0;i<n;i++) c[x[y[i]]]++;
        for(i=0;i<m;i++) c[i]+=c[i-1];
        for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
        if(p>=n) break;
        m=p;
    }

}
//LCP
int ranks[maxn],height[maxn],d[maxn][15];
void getHeight()
{
    int i,j,k=0;
    for(i=0;i<=n;i++) ranks[sa[i]]=i;
    for(i=0;i<n;i++) 
    {
        if(k) k--;
        int j=sa[ranks[i]-1];
        while(s[i+k]==s[j+k]) k++;
        height[ranks[i]]=k;
    }
}

void RMQ()
{
	for(int i=1;i<n;i++) d[i][0]=height[i];
	for(int j=1;(1<<j)<n;j++)
		for(int i=1;i+(1<<j)-1<n;i++)
			d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
int LCP(int L,int R)
{
	int a=ranks[L],b=ranks[R];
	if(a>b)
		swap(a,b);
	a++;
    int t=(int)(log(double(b-a+1))/log(2.00));
    return min(d[a][t],d[b-(1<<t)+1][t]);
}

void solve()
{
	int res=0,id=0;
	int l=n/2;
	for(int i=0;i<l;i++)
	{
		int t=LCP(i,n-i-1);
		if(2*t-1>res)
		{
			res=2*t-1;
			id=i;
		}
		if(s[i]==s[i+1])
		{
			t=LCP(i+1,n-i-1);
			if(2*t>res)
			{
				res=2*t;
				id=i;
			}
		}
	}
	cout<<res<<endl;
	id=id-(res-1)/2;
	for(int j=0;j<res;j++)
	{
		cout<<s[id+j];
	}
	cout<<endl;
}
int main()
{
	cin>>s;
	n=strlen(s);
	s[n]=24;
	for(int i=0;i<n;i++)
	{
		s[n+i+1]=s[n-i-1];
	}
	n=2*n+1;
	s[n+1]='\0';
	build_sa(n+1,200);
	getHeight();
	RMQ();
	solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值