zoukankan      html  css  js  c++  java
  • [模板]后缀自动机

    传送门

    Description

    给定一个只包含小写字母的字符串(S),

    请你求出 (S) 的所有出现次数不为 (1) 的子串的出现次数乘上该子串长度的最大值。

    Solution

    保持好习惯吧,模板题还是放一下

    SAM的板子,想必是到处都有,反正都比我写的好看。。。

    当初想学SAM的时候,就被某俄文翻译的(20000)字论文吓跑了。。。

    核心?

    • 作为一种可以表示所有后缀的状态的自动机,它得满足状态数尽可能的小
    • SAM的做法:
      1. 每个状态表示所有(Right)集合相同的子串,这里(Right)集合的定义可是一个子串在原串中所有出现位置的右端点的集合。
      2. 对于每个状态,我们定义一个(step),表示该状态所能表示的所有子串中长度最大值
      3. (fa)指针,满足当前串的(Right)集合是(fa)指向的状态的真子集,且是最大的那一个,可以发现,(fa)指针所指向的状态一定是当前状态子串的一个后缀。
      4. 在线加点,每次加点后最多只会增加两个新的状态

    Code 

    #include<bits/stdc++.h>
    #define ll long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    class Suf_Automation
    {
    	#define MX 2000005
    	private:
    		int c[MX][26],fa[MX],step[MX],v[MX],rk[MX],val[MX];
    		int last,cnt,n;
    		ll ans=0;
    	public:
    		inline void init(int len)
        	{
            	cnt=last=1;n=len;
            	for(int i=1;i<=n<<1;++i)
                memset(c[i],0,sizeof c[i]),step[i]=fa[i]=v[i]=val[i]=0;
        	}
    		void Insert(int x)
    		{
        		int p=last,np=++cnt;step[np]=step[p]+1;val[np]=1;
            	for(;p&&!c[p][x];p=fa[p]) c[p][x]=np;
            	if(!p) fa[np]=1;
            	else 
            	{
            	    int q=c[p][x];
            	    if(step[q]==step[p]+1) fa[np]=q;
            	    else 
            	    {
            	        int nq=++cnt;step[nq]=step[p]+1;
            	        memcpy(c[nq],c[q],sizeof c[q]);
            	        fa[nq]=fa[q];fa[np]=fa[q]=nq;
            	        for(;c[p][x]==q;p=fa[p]) c[p][x]=nq;
            	    }    
            	}
            	last=np;
    		}
    		inline void Query()
    		{
    			register int i;
    			for(i=1;i<=cnt;++i) ++v[step[i]];
            	for(i=1;i<=n;++i) v[i]+=v[i-1];
            	for(i=1;i<=cnt;++i) rk[v[step[i]]--]=i;
            	for(i=cnt;i;--i)
    			{
    				val[fa[rk[i]]]+=val[rk[i]];
    				if(val[rk[i]]>1) ans=max(ans,1ll*val[rk[i]]*step[rk[i]]);
    			}
    			val[1]=0;
        		printf("%lld
    ",ans);
    		}
    	#undef MX
    }pac;
    #define MN 1000005
    char s[MN];
    int main()
    {
    	scanf("%s",s+1);
    	register int i,n=strlen(s+1);
    	pac.init(n);
    	for(i=1;i<=n;++i) pac.Insert(s[i]-'a');
    	pac.Query();
    	return 0;
    }
    

    SAM的作用?

    很好的维护子串信息的工具嘛。

    下面附上一个简单的,查询某个串的出现次数?

    int Calc(char*s,int l,int r)
    {
    	int x=1;
        for(int i=l;i<=r;++i)
        	if(!c[x][s[i]-'a']) return 0;
            else x=c[x][s[i]-'a'];    
        return val[x];
    }
    

    可以发现,顺着SAM查询子串,到达的状态是所有与查询的子串出现情况相类似(出现次数肯定是相同的)的子串集合,当然,待查子串也在这个集合里辣。



    Blog来自PaperCloud,未经允许,请勿转载,TKS!

  • 相关阅读:
    substr函数
    Oracle 日期处理
    translate函数使用
    nvl函数
    random随机函数
    case语句
    列的拼接
    并行HASH JOIN小表广播问题
    WITH AS 优化逻辑读
    【hihoCoder挑战赛28 A】异或排序
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/10321775.html
Copyright © 2011-2022 走看看