贴(改)模板大集合系列
大概包括了pam的这些问题:
1.sz的意义:本质不同的回文子串个数
2.cnt的意义:当前回文子串出现的次数
3.num的意义:靠最右边的回文子串个数、新加一个字符产生的回文子串个数、暴力fail的层数
4.half的意义:长度小于等于当前回文子串的一半的回文后缀的节点
5.支持两边加字符的回文自动机的做法:维护一左一右两个last
Tsinsen A1280
问题描述
顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。
输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。
#include
using namespace std;
typedef long long ll;
const int maxn=100007;
char s[maxn];
struct PAM{
int next[maxn][26];
int fail[maxn];
int cnt[maxn];
int len[maxn];
int S[maxn];
int last,n,sz;
int newnode(int l){
for(int i=0;i<26;i++){
next[sz][i]=0;
}
cnt[sz]=0;
len[sz]=l;
return sz++;
}
void init(){
sz=0;
newnode(0);
newnode(-1);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
}
int getfail(int x){
while(S[n-len[x]-1]!=S[n])x=fail[x];
return x;
}
void insert(char ch){
int c=ch-'a';
S[++n]=c;
int u=getfail(last);
if(!next[u][c]){
int v=newnode(len[u]+2);
fail[v]=next[getfail(fail[u])][c];
next[u][c]=v;
}
last=next[u][c];
cnt[last]++;
}
void count(){
for(int i=sz-1;i;i--){
cnt[fail[i]]+=cnt[i];
}
}
}pam;
int pre[maxn];
int main(){
int ans=0;
scanf("%s",s);
int n=strlen(s);
pam.init();
for(int i=0;i0;i--){
pam.insert(s[i]);
ans=max(ans,pre[i-1]+pam.len[pam.last]);
}
printf("%lld\n",ans);
}
Tsinsen A1393
问题描述
给你一个长度 n (1 ≤ n ≤ 2·106) 的只由小写字母组成的字符串s。
我们考虑s的所有连续且回文的子串集合P。位置不同但内容相同的两个串算作不同。
问从P中选出两个串且他们在s中有公共位置的方法数有几个?
#include
using namespace std;
typedef long long ll;
const int mod=51123987;
const int inv2=(mod+1)/2;
const int maxn=2000007;
char s[maxn];
struct PAM{
int next[maxn][26];
int fail[maxn];
int cnt[maxn];
int num[maxn];
int len[maxn];
int S[maxn];
int last,n,sz;
int newnode(int l){
for(int i=0;i<26;i++){
next[sz][i]=0;
}
cnt[sz]=0;
len[sz]=l;
return sz++;
}
void init(){
sz=0;
newnode(0);
newnode(-1);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
}
int getfail(int x){
while(S[n-len[x]-1]!=S[n])x=fail[x];
return x;
}
void insert(char ch){
int c=ch-'a';
S[++n]=c;
int u=getfail(last);
if(!next[u][c]){
int v=newnode(len[u]+2);
fail[v]=next[getfail(fail[u])][c];
next[u][c]=v;
num[v]=num[fail[v]]+1;
}
last=next[u][c];
cnt[last]++;
}
ll count(){
ll ret=0;
for(int i=sz-1;i;i--){
cnt[fail[i]]+=cnt[i];
ret=(ret+cnt[i])%mod;
}
return ret;
}
}pam;
ll suf[maxn];
int main(){
int ans=0;
int n;
scanf("%d",&n);
scanf("%s",s);
pam.init();
for(int i=n-1;i>=0;i--){
pam.insert(s[i]);
suf[i]=(suf[i+1]+pam.num[pam.last])%mod;
}
ll tot=suf[0]*(suf[0]-1+mod)%mod*inv2%mod;
pam.init();
ll rev=0;
for(int i=0;i
ACM-ICPC 2018 南京赛区网络预赛 I.Skr
问题描述
求本质不同回文子串的hash和
#include
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=2000007;
char s[maxn];
ll pw[maxn];
ll ans=0;
struct PAM{
int next[maxn][10];
int fail[maxn];
int cnt[maxn];
int len[maxn];
ll val[maxn];
int S[maxn];
int last,n,sz;
int newnode(int l){
for(int i=0;i<10;i++){
next[sz][i]=0;
}
cnt[sz]=0;
len[sz]=l;
val[sz]=0;
return sz++;
}
void init(){
sz=0;
newnode(0);
newnode(-1);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
}
int getfail(int x){
while(S[n-len[x]-1]!=S[n])x=fail[x];
return x;
}
void insert(char ch){
int c=ch-'0';
S[++n]=c;
int u=getfail(last);
if(!next[u][c]){
int v=newnode(len[u]+2);
if(len[u]==-1){
val[v]=c;
}
else{
val[v]=(val[u]*10+c+c*pw[len[u]+1])%mod;
}
ans=(ans+val[v])%mod;
fail[v]=next[getfail(fail[u])][c];
next[u][c]=v;
}
last=next[u][c];
cnt[last]++;
}
void count(){
for(int i=sz-1;i;i--){
cnt[fail[i]]+=cnt[i];
}
}
}pam;
int main(){
scanf("%s",s);
pam.init();
pw[0]=1;
for(int i=1;i
Bzoj 2342
问题描述
求最长双倍回文偶回文子串长度
#include
using namespace std;
typedef long long ll;
const int maxn=500007;
char s[maxn];
struct edge{
int to,next;
}e[maxn];
int head[maxn];
int cnt;
void init(){
cnt=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v){
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt++;
}
struct PAM{
int next[maxn][26];
int fail[maxn];
int cnt[maxn];
int num[maxn];
int len[maxn];
int S[maxn];
int last,n,sz;
int newnode(int l){
for(int i=0;i<26;i++){
next[sz][i]=0;
}
cnt[sz]=0;
len[sz]=l;
return sz++;
}
void init(){
sz=0;
newnode(0);
newnode(-1);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
addedge(1,0);
}
int getfail(int x){
while(S[n-len[x]-1]!=S[n])x=fail[x];
return x;
}
void insert(char ch){
int c=ch-'a';
S[++n]=c;
int u=getfail(last);
if(!next[u][c]){
int v=newnode(len[u]+2);
fail[v]=next[getfail(fail[u])][c];
addedge(fail[v],v);
next[u][c]=v;
num[v]=num[fail[v]]+1;
}
last=next[u][c];
cnt[last]++;
}
void count(){
for(int i=sz-1;i;i--){
cnt[fail[i]]+=cnt[i];
}
}
}pam;
int vis[maxn];
int ans;
void dfs(int u){
if(pam.len[u]%4==0&&vis[pam.len[u]/2]){
ans=max(ans,pam.len[u]);
}
for(int i=head[u];~i;i=e[i].next){
int v=e[i].to;
vis[pam.len[v]]++;
dfs(v);
vis[pam.len[v]]--;
}
}
int main(){
int n;
scanf("%d",&n);
scanf("%s",s);
init();
pam.init();
for(int i=0;i
Hdu 5421
问题描述
维护一个字符串,支持4种操作
1. 左增加一个字符
2. 右增加一个字符
3. 查询本质不同的回文子串个数
4. 查询回文子串个数
#include
using namespace std;
typedef long long ll;
const int maxn=200007;
typedef long long ll;
struct PAM{
int next[maxn][26];
int fail[maxn];
int cnt[maxn];
int num[maxn];
int len[maxn];
int S[maxn];
int last1,last2,l,r,sz;
int newnode(int l){
for(int i=0;i<26;i++){
next[sz][i]=0;
}
cnt[sz]=0;
len[sz]=l;
return sz++;
}
void init(){
sz=0;
newnode(0);
newnode(-1);
last1=last2=0;
l=100002;
r=l-1;
memset(S,-1,sizeof(S));
fail[0]=1;
}
int getfail1(int x){
while(S[l+len[x]+1]!=S[l])x=fail[x];
return x;
}
int getfail2(int x){
while(S[r-len[x]-1]!=S[r])x=fail[x];
return x;
}
void insertbefore(char ch){
int c=ch-'a';
S[--l]=c;
int u=getfail1(last1);
if(!next[u][c]){
int v=newnode(len[u]+2);
fail[v]=next[getfail1(fail[u])][c];
next[u][c]=v;
num[v]=num[fail[v]]+1;
}
last1=next[u][c];
cnt[last1]++;
if(len[last1]==r-l+1)last2=last1;
}
void insertafter(char ch){
int c=ch-'a';
S[++r]=c;
int u=getfail2(last2);
if(!next[u][c]){
int v=newnode(len[u]+2);
fail[v]=next[getfail2(fail[u])][c];
next[u][c]=v;
num[v]=num[fail[v]]+1;
}
last2=next[u][c];
cnt[last2]++;
if(len[last2]==r-l+1)last1=last2;
}
void count(){
for(int i=sz-1;i;i--){
cnt[fail[i]]+=cnt[i];
}
}
}pam;
int main(){
int n;
while(~scanf("%d",&n)){
pam.init();
ll sum=0;
for(int i=1;i<=n;i++){
int op;
char str[3];
scanf("%d",&op);
if(op==1){
scanf("%s",str);
pam.insertbefore(str[0]);
sum+=pam.num[pam.last1];
}
else if(op==2){
scanf("%s",str);
pam.insertafter(str[0]);
sum+=pam.num[pam.last2];
}
else if(op==3){
printf("%d\n",pam.sz-2);
}
else {
printf("%lld\n",sum);
}
}
}
}
CF 245H
题目描述
给一个长为5000的字符串,多次查询区间回文子串个数
#include
using namespace std;
typedef long long ll;
const int maxn=5007;
char s[maxn];
struct PAM{
int next[maxn][26];
int fail[maxn];
int cnt[maxn];
int num[maxn];
int len[maxn];
int S[maxn];
int last,n,sz;
int newnode(int l){
for(int i=0;i<26;i++){
next[sz][i]=0;
}
cnt[sz]=0;
len[sz]=l;
return sz++;
}
void init(){
sz=0;
newnode(0);
newnode(-1);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
}
int getfail(int x){
while(S[n-len[x]-1]!=S[n])x=fail[x];
return x;
}
void insert(char ch){
int c=ch-'a';
S[++n]=c;
int u=getfail(last);
if(!next[u][c]){
int v=newnode(len[u]+2);
fail[v]=next[getfail(fail[u])][c];
next[u][c]=v;
num[v]=num[fail[v]]+1;
}
last=next[u][c];
cnt[last]++;
}
void count(){
for(int i=sz-1;i;i--){
cnt[fail[i]]+=cnt[i];
}
}
}pam;
int n;
int ans[5003][5003];
int main(){
scanf("%s",s);
n=strlen(s);
for(int l=0;l
CodeChef Palindromeness
题目描述
每个字符串的价值定义如下:
#include
using namespace std;
typedef long long ll;
const int maxn=100007;
char s[maxn];
struct PAM{
int next[maxn][26];
int fail[maxn];
int cnt[maxn];
int num[maxn];
int len[maxn];
int dp[maxn];
int sum[maxn];
int S[maxn];
int half[maxn];
int last,n,sz;
int newnode(int l){
for(int i=0;i<26;i++){
next[sz][i]=0;
}
cnt[sz]=0;
len[sz]=l;
dp[sz]=0;
sum[sz]=0;
return sz++;
}
void init(){
sz=0;
newnode(0);
newnode(-1);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
}
int getfail(int x){
while(S[n-len[x]-1]!=S[n])x=fail[x];
return x;
}
int gethalf(int x,int l){
while(S[n-len[x]-1]!=S[n]||len[x]+2>l)x=fail[x];
return x;
}
void insert(char ch){
int c=ch-'a';
S[++n]=c;
int u=getfail(last);
if(!next[u][c]){
int v=newnode(len[u]+2);
fail[v]=next[getfail(fail[u])][c];
next[u][c]=v;
num[v]=num[fail[v]]+1;
if(len[v]==1)half[v]=1;
else half[v]=next[gethalf(half[u],len[v]/2)][c];
if(len[half[v]]==len[v]/2)dp[v]=dp[half[v]]+1;
else dp[v]=1;
sum[v]=dp[v]+sum[fail[v]];
}
last=next[u][c];
cnt[last]++;
}
void count(){
for(int i=sz-1;i;i--){
cnt[fail[i]]+=cnt[i];
}
}
}pam;
ll ans;
int n;
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%s",s);
n=strlen(s);
pam.init();
ans=0;
for(int i=0;i
Aizu 2292
题目描述
求两个字符串的公共回文子串对数
#include
using namespace std;
typedef long long ll;
const int maxn=100007;
char s1[maxn],s2[maxn];
struct PAM{
int next[maxn][26];
int fail[maxn];
int cnt[maxn];
int num[maxn];
int len[maxn];
int S[maxn];
int last,n,sz;
int newnode(int l){
for(int i=0;i<26;i++){
next[sz][i]=0;
}
cnt[sz]=0;
len[sz]=l;
return sz++;
}
void init(){
sz=0;
newnode(0);
newnode(-1);
last=0;
n=0;
S[n]=-1;
fail[0]=1;
}
int getfail(int x){
while(S[n-len[x]-1]!=S[n])x=fail[x];
return x;
}
void insert(char ch){
int c=ch-'A';
S[++n]=c;
int u=getfail(last);
if(!next[u][c]){
int v=newnode(len[u]+2);
fail[v]=next[getfail(fail[u])][c];
next[u][c]=v;
num[v]=num[fail[v]]+1;
}
last=next[u][c];
cnt[last]++;
}
void count(){
for(int i=sz-1;i;i--){
cnt[fail[i]]+=cnt[i];
}
}
}pam1,pam2;
ll ans;
int n,m;
void dfs(int u1,int u2){
if(u1>1)ans+=1ll*pam1.cnt[u1]*pam2.cnt[u2];
for(int i=0;i<26;i++){
int v1=pam1.next[u1][i];
int v2=pam2.next[u2][i];
if(v1&&v2)dfs(v1,v2);
}
}
int main(){
scanf("%s%s",s1,s2);
n=strlen(s1);
m=strlen(s2);
pam1.init();
pam2.init();
for(int i=0;i