zoukankan      html  css  js  c++  java
  • [Luogu P3649] [APIO2014]回文串(后缀自动机)(或回文自动机)

    [Luogu P3649] [APIO2014]回文串(后缀自动机)(或回文自动机)

    题面

    给出一个长度为(n)的字符串,求它的所有回文子串的出现次数乘以长度的最大值

    (1 leq n leq 3 imes 10^5)

    分析

    SAM做法:

    方法来自2015年国家集训队论文。吐槽:为什么网上的大部分题解都是SAM+Manacher啊,明明一个SAM就可以很简洁解决。

    我们对原串构造后缀自动机,求出每个节点right(endpos)集合中的最大值,即最后一次出现的结束位置,记为(maxpos).然后把反串放到自动机上跑,如果当前的匹配串([i,i+len-1])覆盖了当前节点的(maxpos),那么([i,maxpos])是一个满足条件的回文子串.另外为了求它的出现次数,还需要求出right集合的元素个数。这些都可以通过自底向上遍历parent树求出。

    PAM做法:

    先建出回文自动机,计算一下每个节点代表的串出现了多少次。然后在fail树上从深到浅累加出现次数。(因为若S出现,那么串S的子串也出现),相当于加上后缀的贡献。

    因为PAM里的每个节点都代表回文串,直接扫一遍求最大值即可。

    代码

    SAM:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define maxc 26
    #define maxn 600000
    using namespace std;
    typedef long long ll;
    struct SAM{
    #define len(x) (t[x].len)
    #define link(x) (t[x].link) 
    	struct node{
    		int ch[maxc];
    		int link;
    		int len;
    		int maxpos;//right集合中的最大值 
    		int cnt;//该位置子串的出现次数,即right集合大小,在parent树上可求出。 
    	}t[maxn+5];
    	const int root=1;
    	int ptr=1;
    	int last=root;
    	void extend(char ch,int pos){
    		int c=ch-'a';
    		int p=last,cur=++ptr;
    		len(cur)=len(p)+1;
    		t[cur].maxpos=pos; 
    		t[cur].cnt=1;
    		//cur和cur在parent树上的祖先的right集合中都会多出元素pos
    		//打一个标记,等建完之后再向上合并 
    		while(p&&t[p].ch[c]==0){
    			t[p].ch[c]=cur;
    			p=link(p);
    		}
    		if(p==0) link(cur)=root;
    		else{
    			int q=t[p].ch[c];
    			if(len(p)+1==len(q)) link(cur)=q;
    			else{
    				int clo=++ptr;
    				t[clo]=t[q];
    				t[clo].cnt=0;
    				//复制clo的时候,一定要把clo的和right集合有关的标记清空 
    				//因为复制clo的原因是len(p)+1<len(q),即状态q代表的串中,长度不超过len(p)+1的串会在当前串结尾出现,right集合会多出元素pos 
    				//但是长度超过len(p)+1的串就不会被cur状态的right集合影响,所以拆完点记得清零 
    				link(q)=link(cur)=clo;
    				len(clo)=len(p)+1;
    				while(p&&t[p].ch[c]==q){
    					t[p].ch[c]=clo;
    					p=t[p].link;
    				}
    			}
    		}
    		last=cur;
    	}
    	void insert(char *s){
    		int len=strlen(s+1);
    		for(int i=1;i<=len;i++) extend(s[i],i);
    	}
    	
    	void topo_sort(){
    		queue<int>q;
    		static int in[maxn+5];
    		for(int i=1;i<=ptr;i++) in[link(i)]++;//实际上是拓扑序的逆序
    		for(int i=1;i<=ptr;i++) if(!in[i]) q.push(i); 
    		while(!q.empty()){
    			int x=q.front();
    			q.pop();
    			if(link(x)==0) continue;
    			t[link(x)].maxpos=max(t[link(x)].maxpos,t[x].maxpos);
    			t[link(x)].cnt+=t[x].cnt;
    			in[link(x)]--;
    			if(!in[link(x)]) q.push(link(x));
    		}
    	}
    	ll solve(char *s){
    		static bool vis[maxn+5]; 
    		ll ans=0;
    		int len=strlen(s+1); 
    		int matl=0;//匹配长度 
    		int x=root;
    		insert(s);
    		topo_sort();
    		for(int i=len;i>=1;i--){
    			int c=s[i]-'a';
    			while(x&&!t[x].ch[c]){ 
    				x=link(x);
    				matl=len(x);
    			}
    			if(t[x].ch[c]){
    				matl++;
    				x=t[x].ch[c];
    			}
    			int tmp=matl;
    			for(int y=x;y!=root&&!vis[y];y=link(y),tmp=len(y)){
    				if(y!=x) vis[y]=1;
    				if(t[y].maxpos>=i&&t[y].maxpos<=i+tmp-1) ans=max(ans,1ll*(t[y].maxpos-i+1)*t[y].cnt);
    					//如果反串的匹配串[i,i+tmp-1]覆盖了maxpos,那[i,maxpos]就是一个回文串 
    			}
    		}	
    		return ans;
    	}
    #undef len
    #undef link
    }T; 
    
    char str[maxn+5];
    int main(){
    	scanf("%s",str+1);
    	printf("%lld
    ",T.solve(str));
    }
    
    

    PAM:

    //https://www.luogu.com.cn/problem/P3649
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxc 26
    #define maxn 300000
    using namespace std;
    typedef long long ll;
    int n;
    char s[maxn+5];
    struct PAM{
    	struct node{
    		int fail;
    		int ch[maxc];
    		int cnt;//覆盖次数 
    		int len;
    	}t[maxn+5];
    	int ptr;
    	int last;
    	void ini(){
    		ptr=1;
    		t[0].len=0;
    		t[0].fail=1;
    		t[1].len=-1;
    		t[1].fail=0;
    		last=0;
    	}
    	int get_fail(int x,int n){
    		while(s[n-t[x].len-1]!=s[n]) x=t[x].fail;
    		return x;
    	}
    	void insert(int c,int pos){
    		int p=get_fail(last,pos);
    		if(t[p].ch[c]==0){
    			int cur=++ptr;
    			t[cur].len=t[p].len+2;
    			t[cur].fail=t[get_fail(t[p].fail,pos)].ch[c];
    			t[p].ch[c]=cur;
    		} 
    		t[t[p].ch[c]].cnt++; 
    		last=t[p].ch[c];
    	}
    	inline ll calc(){
    		ll ans=0;
    		for(int i=ptr;i>=1;i--)  t[t[i].fail].cnt+=t[i].cnt;//回文自动机的性质,似乎fail树中后代的编号一定更大 
    		for(int i=1;i<=ptr;i++) ans=max(ans,1ll*t[i].cnt*t[i].len);
    		return ans;
    	}
    }T;
    int main(){
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	T.ini();
    	for(int i=1;i<=n;i++) T.insert(s[i]-'a',i);
    	printf("%lld
    ",T.calc());
    }
    
    
  • 相关阅读:
    habse与Hadoop兼容性问题
    java读取TXT文件(硬核区分编码格式)
    ffmpeg相关用法
    java服务器间http通讯,同时传输文件流和数据,并接收返回的文件流或数据
    js 获取正整数各个位上的数字
    解决Input输入中文重复出现拼音
    为什么 io 包一般以 byte 数组做为处理单位?
    Vue3 + Ts 封装axios
    Vue3 ant design 使用Moment.js日期格式化的实现
    Vue3 NProgress进度条
  • 原文地址:https://www.cnblogs.com/birchtree/p/12369393.html
Copyright © 2011-2022 走看看