Poj 3376 回文串,扩展kmp

题目链接:http://poj.org/status?problem_id=3376

 

题意给出一连串多组字符串,问这些字符串两两相连能形成多少个回文串。

 

参考别人的思路,看了下扩展kmp,才明白怎么解决这一题~

 

如果两个字符串的公共前缀为其中某一个的整,并且另一个串的余下部分为回文串,则一个字符串与另一个字符串的逆序串边接可成一回文串。

 

所以这题中,将字符串分原串和逆串进行处理,在字典树中找公共前缀, 而非公共前缀部分的回文串判断,用扩展kmp解决。

 

Code:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define N 2000012

char s[N];
int len,n;
int f[N],t[N];
typedef struct talT{
	int c,leaf;
	talT *nxt[27];
}Trie;

Trie Memory[N*3];
Trie *r;
int size;
bool bis[N];
int nxt[N],ext[N];
Trie *NewTrie()
{
	return &Memory[size++];
}
void Insert(int ix)
{
	int i,a;
	Trie *cur=r;
	for(i=f[ix];i<t[ix];i++){
		cur->c+=bis[i];
		a=s[i]&31;
		if(!cur->nxt[a])
			cur->nxt[a]=NewTrie();
		cur=cur->nxt[a];
	}
	cur->leaf++;
}
void ExtendKMP(char s[],char t[],int l,int ix)
{

	int i,j,k;
	int Len,L;

	//next
	j=0;
	while(t[j]==t[1+j]&&j+1<l) j++;
	nxt[1]=j,k=1;
	for(i=2;i<l;i++){
		Len=k+nxt[k],L=nxt[i-k];
		if(Len>L+i) nxt[i]=L;
		else{
			j=Len-i>0?Len-i:0;
			while(t[i+j]==t[j]&&i+j<l) j++;
			nxt[i]=j,k=i;
		}
	}
	//extend
	j=0;
	while(s[j]==t[j]&&j<l) j++;
	ext[0]=j,k=0;
	for(i=1;i<l;i++){
		Len=k+ext[k],L=nxt[i-k];
		if(Len>L+i) ext[i]=L;
		else{
			j=Len-i>0?Len-i:0;
			while(s[i+j]==t[j]&&i+j<l) j++;
			ext[i]=j,k=i;
		}
	}
	for(i=0;i<l;i++){
		if(ext[i]==l-i)
			bis[ix+i]=1;
	}
}
__int64 Query()
{
	int i,j;
	__int64 ret;
	int a;
	Trie *cur;

	for(ret=i=0;i<n;i++){
		cur=r;
		for(j=t[i];j<f[i+1];j++){
			a=s[j]&31;
			cur=cur->nxt[a];
			if(!cur) break;
			if(bis[j+1]||j+1==f[i+1])
				ret+=cur->leaf;
		}
		if(cur) ret+=cur->c;
	}
	return ret;
}
int main()
{
	int i,j,l;
	scanf("%d",&n);
	len=0;size=0;
	r=NewTrie();
	for(i=0;i<n;i++){
		scanf("%d%s",&l,s+len);

		for(j=0;j<l;j++)
			s[len+(l<<1)-j-1]=s[len+j];
		ExtendKMP(s+len,s+len+l,l,len);
		ExtendKMP(s+len+l,s+len,l,len+l);
		f[i]=len;
		t[i]=len+l;
		len+=l<<1;
		Insert(i);
	}
	f[i]=len;
	printf("%I64d\n",Query());
	return 0;
}


 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值