字符串

KMP

两个单整串相互匹配。

#include<bits/stdc++.h>
using namespace std;

const int N=1e3+5;
string s1,s2;
int nxt[N],ans,len1,len2;

int main()
{
    cin>>s1>>s2;
    len1=s1.length();
    len2=s2.length();
    for(int i=1,j=0;i<len2;++i)
    {
    while(j&&s2[i]!=s2[j]) j=nxt[j];
    nxt[i+1]=s2[i]==s2[j]?++j:0;
    }
    ans=0;
    for(int i=0,j=0;i<len1;++i)
    {
    while(j&&s1[i]!=s2[j]) j=nxt[j];
    if(s1[i]==s2[j]&&++j==len2)  ans++;
    }
    cout<<ans<<"\n";	
	
}

结论1:一个长度为n的字符串,最短循环节的长度为n-nxt[n]。

结论2:使每个nxt[i]=nxt[nxt[i]],i-nxt[i]可以得到最大周期。即Q∈≠Q,A∈QQ。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+5;
string s2;
int nxt[N],n;
ll ans;

int main()
{
    cin>>n>>s2;
    for(int i=1,j=0;i<n;++i)
    {
    	while(j&&s2[i]!=s2[j]) j=nxt[j];
  		nxt[i+1]=s2[i]==s2[j]?++j:0;
	}
	for(int i=2;i<=n;++i)
	if(nxt[nxt[i]]) nxt[i]=nxt[nxt[i]];
	for(int i=2;i<=n;++i)
	if(nxt[i]) ans+=(ll)i-nxt[i];
	cout<<ans;
}

TRIE

一个整串和多个串匹配

#include<bits/stdc++.h>
using namespace std;
#define maxn 100000+5

string s;
int n,k,tot=0;
int tree[maxn][26]={ };
bool end[maxn];

void charu()
{
	int p=0;
	for(int i=0;i<s.length();i++)
	{
		if(!tree[p][s[i]-'a']) tree[p][s[i]-'a']=++tot;
		p=tree[p][s[i]-'a'];
	}
	end[p]=1;
	return;
}

bool search()
{
	int p=0;
	for(int i=0;i<s.length();i++)
	{
		if(!tree[p][s[i]-'a']) return 0;
		p=tree[p][s[i]-'a'];
	}
	return end[p];
}

int main()
{
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		cin>>s;
		charu();
	}
	for(int i=1;i<=k;i++)
	{
		cin>>s;
		if(search()) printf("Yes\n");
		else printf("No\n");
	}
}

异或树

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,te,tr,ans,tail[N],xo[N],trie[N*35][2];
struct e_
{
	int v,w,pre;
}e[N<<1];

inline void add(int u,int v,int w)
{
	e[++te]={v,w,tail[u]};
	tail[u]=te;
}

void dfs(int u,int fa)
{
	for(int i=tail[u];i;i=e[i].pre)
	{
		int v=e[i].v,w=e[i].w;
		if(v==fa) continue;
		xo[v]=xo[u]^w;
		dfs(v,u);
	}
}

void add(int x)
{
	int p=0;
	for(int i=1<<30;i;i>>=1)
	{
		bool y=x&i;
		if(!trie[p][y]) trie[p][y]=++tr;
		p=trie[p][y];
	}
}

int calc(int x)
{
	int res=0;
	int p=0;
	for(int i=1<<30;i;i>>=1)
	{
		bool y=x&i;
		if(trie[p][y^1]) p=trie[p][y^1],res+=i;
		else p=trie[p][y];
	}
	return res;
}

int main()
{
	scanf("%d",&n);
	for(int i=1,u,v,w;i<n;++i)
	{
		scanf("%d %d %d",&u,&v,&w);
		add(u,v,w);
		add(v,u,w);
	}
	
	dfs(1,0);
	//直接存到根节点的值,两点异或运算时,lca到根节点部分相当于异或两次,等同于毫无变化	
	for(int i=1;i<=n;++i) add(xo[i]);
	for(int i=1;i<=n;++i) ans=max(ans,calc(xo[i]));
	
	printf("%d",ans);
}

查找s在trie上的前缀,以及包含s的串的个数等于:end[图中所有点]+cnt[结束的点]。

通配符

很毒瘤的一道题。

给定n个字符串,q次询问,每次询问一个包含通配符*的字符串可以与多少给定的字符串匹配。

trie树的时候插入一下后缀,然后把含有某后缀的trie树标的L插入这个后缀对应的vector数组,每次就找后缀对应的vector里L[p]到R[p]的范围,L和R是确定边界用的。

整两个哈希要更精准一点。

#include<bits/stdc++.h>
using namespace std;

const int N=3e6+5,M=1e5+5;
const int B1=193,mod1=998244353,B2=131,mod2=19260817;

string s,ss[M];
int n,Q,len;
int tt,num,t[N][26],L[N],R[N];
struct node
{
	int h1,h2;
	friend bool operator<(node a,node b)
	{
		return a.h1!=b.h1?a.h1<b.h1:a.h2<b.h2;
	}
}a[M];

vector<node>q[N];
set<node>sum;
map<node,vector<int> >G;

void add()
{
	int p=0,x;
	q[p].push_back(a[0]);
	for(int i=0;i<len;++i)
	{
		int x=s[i]-'a';
		if(!t[p][x]) t[p][x]=++tt;
		p=t[p][x];
		q[p].push_back(a[i+1]);
	}
}

void DFS(int p)
{
	L[p]=++num;
	
	for(int i=0;i<q[p].size();++i) G[q[p][i]].push_back(L[p]);
	for(int i=0;i<26;++i) if(t[p][i]) DFS(t[p][i]);
	
	R[p]=++num;
}

int ask(int k)
{
	int p=0;
	len=ss[k].length();
	
	for(int i=0;i<len;++i)
	if(ss[k][i]=='*') return upper_bound(G[a[k]].begin(),G[a[k]].end(),R[p])-lower_bound(G[a[k]].begin(),G[a[k]].end(),L[p]);
	else
	{
		int x=ss[k][i]-'a';
		if(!t[p][x]) return 0;
		p=t[p][x];
	}
}

int main()
{
	scanf("%d %d",&n,&Q);
	for(int i=1;i<=n;++i)
	{
		cin>>s;
		len=s.length();
		a[len].h1=a[len].h2=0;
		
		for(int i=len-1;i>=0;--i)
		a[i].h1=(1ll*a[i+1].h1*B1+s[i]-'A')%mod1,
		a[i].h2=(1ll*a[i+1].h2*B2+s[i]-'A')%mod2;
		
		add();
	}
	
	for(int i=1;i<=Q;++i)
	{
		cin>>ss[i];
		len=ss[i].length();
		a[i].h1=a[i].h2=0;
		
		for(int j=len-1;j>=0;--j)
		if(ss[i][j]=='*') break;
		else 
		a[i].h1=(1ll*a[i].h1*B1+ss[i][j]-'A')%mod1,
		a[i].h2=(1ll*a[i].h2*B2+ss[i][j]-'A')%mod2;
		
		sum.insert(a[i]);
	}
	DFS(0);
	for(int i=1;i<=Q;++i) printf("%d\n",ask(i));
}

AC自动机

一个整串的字串与多个字符串匹配,查找trie树上的串有多少个在整串内。

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

const int N=5e5+5;
string s;
int t,n,m,tr,trie[N][30],fail[N],cnt[N];
//字典树||失败指针||某单词出现的次数 

void add()
{
	int p=0;
	for(int i=0;i<s.length();++i)
	{
		if(!trie[p][s[i]-'a']) trie[p][s[i]-'a']=++tr;
		p=trie[p][s[i]-'a'];
	}
	cnt[p]++;
}
void getfail()
{
	deque<int>q;
	//将第二层出现了的字母扔进队列 
	for(int i=0;i<26;++i)
	if(trie[0][i])
		q.push_back(trie[0][i]);
	
	//fail[now]    ->当前节点now的失败指针指向的地方
	//tire[now][i] -> 下一个字母为i+'a'的节点的下标为tire[now][i]
	while(!q.empty())
	{
		int u=q.front();
		q.pop_front();
		//如果这个字母在这里存在,则跳到父节点已匹配串的下一个节点 
		//即,以本点为结束点,存在的某个前缀和本串某后缀相同的串
		//如asha,shp,在asha的h点,先到s对应的fail,即shp中的s点,再到shp中的h点 
		for(int i=0;i<26;++i)
		if(trie[u][i])
		{
			fail[trie[u][i]]=trie[fail[u]][i];
			q.push_back(trie[u][i]);
		}
		else trie[u][i]=trie[fail[u]][i];
		//如果这个字母在这里不存在,就直接连到另一个点的这个点
		//如asha,shp,若给出的串为ashp,则先在asha中到p节点
		//p不存在,则到父节点对应的下一节点,即shp中的h,再到shp中的p
		//如asha,shu,shp,经过两次跳跃同样可以到p点 
	}
}

int query()
{
	int j,p=0,res=0;
	for(int i=0;i<s.length();++i)
	{
		//如果这个节点不存在,由于我们的特殊处理,它会跳转到其他串上存在的地方或者根节点 
		j=p=trie[p][s[i]-'a'];
		//分别以每个点为结束节点跳转,到了根节点或者到过的节点就停止 
		while(j&&cnt[j]!=-1) res+=cnt[j],cnt[j]=-1,j=fail[j];
	}
	return res;
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		memset(cnt,0,sizeof(cnt));
		memset(trie,0,sizeof(trie));
		
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
		{
			cin>>s;
			add();
		} 
//		cout<<tr<<"\n";
		getfail();
		
		cin>>s;
		printf("%d\n",query()); 
	}
	
}

玄武湖畔

Description
在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河。相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中。老人们说,这是玄武神灵将天书藏匿在此。
很多年后,人们终于在进香河地区发现了带有玄武密码的文字。更加神奇的是,这份带有玄武密码的文字,与玄武湖南岸台城的结构有微妙的关联。于是,漫长的破译工作开始了。
经过分析,我们可以用东南西北四个方向来描述台城城砖的摆放,不妨用一个长度为N的序列来描述,序列中的元素分别是‘E’,‘S’,‘W’,‘N’,代表了东南西北四向,我们称之为母串。而神秘的玄武密码是由四象的图案描述而成的M段文字。这里的四象,分别是东之青龙,西之白虎,南之朱雀,北之玄武,对东南西北四向相对应。
现在,考古工作者遇到了一个难题。对于每一段文字,其前缀在母串上的最大匹配长度是多少呢?

Input
第一行有两个整数,N和M,分别表示母串的长度和文字段的个数。
第二行是一个长度为N的字符串,所有字符都满足是E,S,W和N中的一个。
之后M行,每行有一个字符串,描述了一段带有玄武密码的文字。依然满足,所有字符都满足是E,S,W和N中的一个。

Output
输出有M行,对应M段文字。
每一行输出一个数,表示这一段文字的前缀与母串的最大匹配串长度。

Sample Input
7 3

SNNSSNS

NNSS

NNN

WSEE
Sample Output
4

2

0
HINT

对于100%的数据,N<=107,M<=105,每一段文字的长度<=100。
————————————————
版权声明:本文为CSDN博主「Aqua_blue」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Aqua_blue/java/article/details/78799508

#include<bits/stdc++.h>
using namespace std;

const int N=4e7+5,M=1e5+5;
string s,t;
int n,m,tr,trie[N][5],id[M],dep[N],pre[N],fail[N];
bool vis[N];

inline int id_(char a)
{
	if(a=='E') return 1;
	if(a=='S') return 2;
	if(a=='W') return 3;
	return 4;
}
inline void add(int num)
{
	int p=0;
	for(int i=0;i<s.length();++i)
	{
		int &x=trie[p][id_(s[i])];
		if(!x) x=++tr,pre[x]=p,dep[x]=dep[p]+1;
		p=x; 
	}
	id[num]=p;
}

void getfail()
{
	deque<int>q;
	for(int i=1;i<=4;++i)
	if(trie[0][i]) q.push_back(trie[0][i]);
	
	while(!q.empty())
	{
		int u=q.front();
		q.pop_front();
		
		for(int i=1;i<=4;++i)
		if(trie[u][i]) 
		{
			fail[trie[u][i]]=trie[fail[u]][i];
			q.push_back(trie[u][i]);
		}
		else trie[u][i]=trie[fail[u]][i];
	}
}

void query()
{
	int j,p=0;
	for(int i=0;i<t.length();++i)
	{
		j=p=trie[p][id_(t[i])];
		while(j&&!vis[j]) vis[j]=1,j=fail[j];
	}
}

int get(int x)
{
	if(vis[x]||!x) return dep[x];
	return get(pre[x]);
}

int main()
{
	scanf("%d %d",&n,&m);
	cin>>t;
	for(int i=1;i<=m;++i)
	{
		cin>>s;
		add(i);
	}
	getfail();
	query();
	for(int i=1;i<=m;++i) printf("%d\n",get(id[i]));
}

删除特定字串

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
string s,t;
int n,m,tr,ta,ans[N],id[N],trie[N][32],fail[N],end[N];

inline void add()
{
	int p=0;
	for(int i=0;i<s.length();++i)
	{
		int &x=trie[p][s[i]-'a'];
		if(!x) x=++tr;
		p=x; 
	}
	end[p]=s.length();
}

void getfail()
{
	deque<int>q;
	for(int i=0;i<26;++i)
	if(trie[0][i]) q.push_back(trie[0][i]);
	
	while(!q.empty())
	{
		int u=q.front();
		q.pop_front();
		for(int i=0;i<26;++i)
		if(trie[u][i]) 
		{
			fail[trie[u][i]]=trie[fail[u]][i];
			q.push_back(trie[u][i]);
		}
		else trie[u][i]=trie[fail[u]][i];
	}
}

void query()
{
	int j,p=0;
	
	for(int i=0;i<t.length();++i)
	{
		ans[++ta]=i;
		id[i]=j=p=trie[p][t[i]-'a'];

		if(end[j]) ta-=end[j],p=id[ans[ta]];
	}
	
	for(int i=1;i<=ta;++i) printf("%c",t[ans[i]]);
}

int main()
{
	cin>>t;
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		cin>>s;
		add();
	}
	getfail();
	query();
}

最短母串

#include<bits/stdc++.h>
#define ts cout<<"ok"<<endl
#define ll long long
#define hh puts("")
using namespace std;
int n,f[5005][13],max_status,top;
string s[105],g[5005][13],ans[105];
inline int read(){
    int ret=0,ff=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') ff=-ff;ch=getchar();}
    while(isdigit(ch)){ret=(ret<<3)+(ret<<1)+(ch^48);ch=getchar();}
    return ret*ff;
}
inline int check(int x,int y){
    if(s[x].find(s[y])!=s[x].npos) return -1;//x包含y 
    for(int i=0;i<s[x].size();i++){//枚举j从哪一位开始与k重叠
        bool fl=1;
        for(int j=i;j<s[x].size();j++){
            if(s[y][j-i]!=s[x][j]){
                fl=0;
                break;
            }
        }
        if(fl) return s[x].size()-i;//返回重叠部分长 
    }
    return 0;
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++) cin>>s[i];
    memset(f,0x3f,sizeof(f));
    f[0][0]=0;
    for(int i=1;i<=n;i++) f[1<<(i-1)][i]=s[i].length(),g[1<<(i-1)][i]=s[i];
    max_status=(1<<n)-1;
    for(int i=0;i<=max_status;i++){
        for(int j=1;j<=n;j++){//以j结尾 
            if(i&(1<<(j-1))){
                for(int k=1;k<=n;k++){//后面接k 
                    if(!(i&(1<<(k-1)))){
                        int to=i|(1<<(k-1));
                        int chong=check(j,k);//重叠部分长度 
                        if(chong==-1){//j包含k 
                            if(f[i][j]<f[to][j]||(f[i][j]==f[to][j]&&g[to][j]>g[i][j])){
                                f[to][j]=f[i][j];
                                g[to][j]=g[i][j];
                            }
                        }
                        else{
                            string t="";
                            for(int p=chong;p<s[k].size();p++) t+=s[k][p];//t是j接上k后多出来的一段字符 
                            t=g[i][j]+t;
                            if((f[i][j]+s[k].size()-chong<f[to][k])||
                            (f[i][j]+s[k].size()-chong==f[to][k]&&g[to][k]>t)){
                                f[to][k]=f[i][j]+s[k].size()-chong;
                                g[to][k]=t;
                            }
                        }
                    }
                }
            }
        }
    }
    int minn=1e9;
    for(int i=1;i<=n;i++){
        if(f[max_status][i]<minn){
            minn=f[max_status][i];
            top=1;
            ans[top]=g[max_status][i];
        }
        else if(f[max_status][i]==minn) ans[++top]=g[max_status][i];
    }
    sort(ans+1,ans+top+1);//排序找字典序最小 
    cout<<ans[1];
    return 0;
}

病毒

Description

二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

示例:

例如如果{011, 11, 00000}为病毒代码段,那么一个可能的无限长安全代码就是010101…。如果{01, 11, 000000}为病毒代码段,那么就不存在一个无限长的安全代码。

任务:

请写一个程序:

l 读入病毒代码;

l 判断是否存在一个无限长的安全代码;

l 将结果输出

Input

第一行包括一个整数n,表示病毒代码段的数目。以下的n行每一行都包括一个非空的01字符串——就是一个病毒代码段。所有病毒代码段的总长度不超过30000。

Output

你应在在文本文件WIN.OUT的第一行输出一个单词:

l TAK——假如存在这样的代码;

l NIE——如果不存在。

Sample Input

3
01
11
00000

Sample Output

NIE

trie图上找到一条可以返回到不是end点的点的路。

#include<bits/stdc++.h>
using namespace std;

const int N=9e5+5;
string s;
int t,n,m,tr,trie[N][30],fail[N];
bool end[N],vis[N],siv[N];
void add()
{
	int p=0;
	for(int i=0;i<s.length();++i)
	{
		if(!trie[p][s[i]-'0']) trie[p][s[i]-'0']=++tr;
		p=trie[p][s[i]-'0'];
	}
	end[p]=1;
}
void getfail()
{
	deque<int>q;
	for(int i=0;i<2;++i)
	if(trie[0][i])
		q.push_back(trie[0][i]);
	
	while(!q.empty())
	{
		int u=q.front();
		q.pop_front();
		for(int i=0;i<2;++i)
		if(trie[u][i])
		{
			int x=trie[u][i];
			fail[x]=trie[fail[u]][i];
			//结束关系的传递!!!!!
			end[x]|=end[fail[x]];
			q.push_back(x);
		}
		else trie[u][i]=trie[fail[u]][i];
	}
}

bool dfs(int x)
{	
	siv[x]=vis[x]=1;
	
	for(int i=0;i<2;++i)
	{
		int y=trie[x][i];
		
		if(vis[y]) return 1;
		if(siv[y]||end[y]) continue;
	
		if(dfs(y)) return 1;
	}
//	if(trie[x][0]&&dfs(trie[x][0])) return 1;
//	if(trie[x][1]&&dfs(trie[x][1])) return 1;
	
	vis[x]=0;
	return 0;
}

int main()
{		
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		cin>>s;
		add();
	}
	
	getfail();
	
	if(dfs(0)) cout<<"TAK";
	else cout<<"NIE";

}

[BZOJ1030]文本生成器

weixin_30782293 2018-08-17 09:10:00 33 收藏
版权
Description
  JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,
他们现在使用的是GW文本生成器v6版。该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文
章—— 也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,
那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。但是,即使按照这样的
标准,使用者现在使用的GW文本生成器v6版所生成的文章也是几乎完全不可读的?。ZYX需要指出GW文本生成器 v6
生成的所有文本中可读文本的数量,以便能够成功获得v7更新版。你能帮助他吗?

Input
  输入文件的第一行包含两个正整数,分别是使用者了解的单词总数N (<= 60),GW文本生成器 v6生成的文本固
定长度M;以下N行,每一行包含一个使用者了解的单词。这里所有单词及文本的长度不会超过100,并且只可能包
含英文大写字母A..Z

Output
  一个整数,表示可能的文章总数。只需要知道结果模10007的值。

Sample Input
2 2
A
B
Sample Output
100

#include<bits/stdc++.h>
using namespace std;

const int N=1e4+5,mod=10007;
string s;
int n,m,tr=0,ans,trie[N][30],fail[N],f[101][N];
bool end[N];

void add()
{
	int p=0;
	for(int i=0;i<s.length();++i)
	{
		if(!trie[p][s[i]-'A']) trie[p][s[i]-'A']=++tr;
			
		p=trie[p][s[i]-'A'];
	}
	
	end[p]=1;
}
void getfail()
{
	deque<int>q;
	for(int i=0;i<26;++i)
	if(trie[0][i])
		q.push_back(trie[0][i]);
	
	while(!q.empty())
	{
		int u=q.front();
		q.pop_front();
		for(int i=0;i<26;++i)
		if(trie[u][i])
		{
			int x=trie[u][i];
			fail[x]=trie[fail[u]][i];
			end[x]|=end[fail[x]];
			q.push_back(x);
		}
		else trie[u][i]=trie[fail[u]][i];
	}
}


int main()
{		
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i)
	{
		cin>>s;
		add();
	}
	
	getfail();
	
	f[0][0]=1;
	
	for(int i=1;i<=m;++i)
	for(int j=0;j<=tr;++j)
	for(int k=0;k<26;++k)
	if(!end[trie[j][k]])
		f[i][trie[j][k]]=(f[i][trie[j][k]]+f[i-1][j])%mod;

	int sum=0;ans=1;
	for(int i=1;i<=m;++i) ans=(ans*26)%mod;
	for(int i=0;i<=tr;++i) sum=(sum+f[m][i])%mod;
	
	printf("%d",(ans-sum+mod)%mod);
}

Manacer算法(最大回文子串)

[国家集训队]最长双回文串

顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为abc,逆序为cba,不相同)。

输入长度为nn的串SS,求SS的最长双回文子串TT,即可将TT分为两部分XX,YY,(|X|,|Y|≥1∣X∣,∣Y∣≥1)且XX和YY都是回文串。

输入格式
一行由小写英文字母组成的字符串SS。

输出格式
一行一个整数,表示最长双回文子串的长度。

输入输出样例
输入 #1 复制
baacaabbacabb
输出 #1 复制
12
说明/提示
【样例说明】

从第二个字符开始的字符串aacaabbacabb可分为aacaa与bbacabb两部分,且两者都是回文串。

对于100%的数据,2≤|S|≤10^52≤∣S∣≤10
5

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
const int N=2e6+5;
string t,s;
int n,k,ans,len[N],l[N],r[N];
ull sl[N];

int main()
{
		cin>>t;
		s="~#";
		
		for(int i=0;i<t.length();++i) s+=t[i],s+='#';
		n=s.length();
		s+='\0';
		ans=0;
		for(int i=1,mid=0,rr=0;i<=n;++i)
		{
			if(i<=rr) len[i]=min(len[mid*2-i],rr-i+1);
			else len[i]=1;
			
			while(s[i-len[i]]==s[i+len[i]]) ++len[i];
			if(i+len[i]-1>rr) rr=i+len[i]-1,mid=i;
			
			l[i-len[i]+1]=max(l[i-len[i]+1],len[i]-1);
			r[i+len[i]-1]=max(r[i+len[i]-1],len[i]-1);
		}
		for(int i=1;i<=n;i+=2) 
			l[i]=max(l[i],l[i-2]-2);
		for(int i=n;i;i-=2)
			r[i]=max(r[i],r[i+2]-2);
		for(int i=1;i<=n;i+=2)
		if(l[i]&&r[i]) ans=max(ans,l[i]+r[i]);
		
		printf("%d",ans);
	
}
posted @ 2020-10-23 20:41  林生。  阅读(111)  评论(0)    收藏  举报