[BZOJ4480] JSOI2013 快樂的jyy


問題背景

JYY在JSOI有很多很多的好朋友,比如PUPPY,KFC還有PUPPUP。因為有了這么多的好朋友,所以JYY每天都很快樂。某天,JYY發現好朋友之間關系的好壞和名字有很大的關系,比如PUPPY和PUPPUP的關系就特別好,但是和KFC的關系就很一般。JYY苦思冥想終於發現了其中的規律,現在JYY想知道兩個朋友之間關系的好壞,你能幫助JYY么?

問題描述

給定兩個字符串A和B,表示JYY的兩個朋友的名字。我們用A(i,j)表示A字符串中從第i個字母到第j個字母所組成的子串。同樣的,我們也可以定義B(x,y)。JYY發現兩個朋友關系的緊密程度,等於同時滿足如下條件的四元組(i,j,x,y)的個數:

1:1<=i<=j<=|A|

2:1<=x<=y<=|B|

3:A(i,j)=B(x,y)

4:A(i,j)是回文串

這里表示字符串A的長度。

JYY希望你幫助他計算出這兩個朋友之間關系的緊密程度。

輸入格式

數據包行兩行由大寫字母組成的字符串A和B
1≤|A|,|B|≤50000。

輸出格式

包含一行一個整數,表示緊密程度,也就是滿足要求的4元組個數

樣例輸入

PUPPY
PUPPUP

樣例輸出

17

鏈接

BZOJ

解析

在這道題中,我們需要統計第二個字符串中的回文串在第一個串中的出現情況。這種問題,我們可以用回文自動機來實現。對第一個串建立回文自動機,然后在建好的fail樹上做一次子樹和統計每一種回文串出現的次數,記為\(sum_i\)。然后用第二個字符串上的每一個位置在回文自動機上匹配,記錄每個點的匹配次數,然后同樣在做一次子樹和記錄每個回文串被匹配的次數,記為\(cnt_i\)。最后答案就是:

\[\sum_{i=1}^n sum_i\times cnt_i \]

代碼

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 50002
using namespace std;
struct PAM{
	int son[26],fail,len,sum,cnt;
}t[N];
char a[N],b[N];
int n,m,i,num,last;
void insert(int p,int x)
{
	int cur=last;
	while(a[p-1-t[cur].len]!=a[p]) cur=t[cur].fail;
	if(!t[cur].son[x]){
		num++;
		int tmp=t[cur].fail;
		while(a[p-1-t[tmp].len]!=a[p]) tmp=t[tmp].fail;
		t[num].len=t[cur].len+2;
		t[num].fail=t[tmp].son[x];
		t[cur].son[x]=num;
	}
	t[t[cur].son[x]].sum++;
	last=t[cur].son[x];
}
void find(int p,int x)
{
	int cur=last;
	while(cur!=1&&(b[p-1-t[cur].len]!=b[p]||!t[cur].son[x])) cur=t[cur].fail;
	if(b[p-1-t[cur].len]==b[p]&&t[cur].son[x]) last=t[cur].son[x],t[last].cnt++;
	else last=1;
}
int main()
{
	scanf("%s%s",a+1,b+1);
	n=strlen(a+1);m=strlen(b+1);
	num=last=1;
	t[0].fail=t[1].fail=1;
	t[1].len=-1;
	for(i=1;i<=n;i++) insert(i,a[i]-'A');
	for(i=num;i>=1;i--) t[t[i].fail].sum+=t[i].sum;
	last=0;
	for(i=1;i<=m;i++) find(i,b[i]-'A');
	for(i=num;i>=1;i--) t[t[i].fail].cnt+=t[i].cnt;
	long long ans=0;
	for(i=2;i<=num;i++) ans+=1LL*t[i].sum*t[i].cnt;
	printf("%lld\n",ans);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM