• 3.27模拟赛 sutoringu(后缀数组)


    (color{white}{mjt是机房模拟赛独自切过题的唯一的人...})
    (应本人要求删掉惹)


    (Description)

    给你(n,k)和长为(n)的字符串(s)。一个区间([l,r])是合法的,当且仅当(s[l...r])能被分成(k)个相同的子串。求有多少个合法区间。
    (n,kleq3 imes10^5)

    (Solution)

    枚举单个子串的长度(len),在(s)上从(1)开始每隔(len)个位置放一个关键点,分成若干块。
    考虑相邻(k)个关键点,以它们开头的(k)个子串是否相同。如果它们两两之间的(LCP)(geq len),显然是合法的。考虑怎么求这(k)个位置的(LCP)
    (SA)(LCP)是两两(rk)之间的最小值,也就是区间([min{rk},max{rk}])之间的最小值。用(set)动态维护一下,查询(LCP)就是(O(1))的了。
    需要枚举(O(nlog n))次,这样这部分复杂度是(O(nlog^2n))

    还有左右端点在块内的情况,也就是跨过了(k-1)个整块。容易发现这(k-1)块的子串一定需要是相同的。同样用(SA)(set)先判一下。
    设左端点(l)在第(p)块内,右端点(r)(p+k)块内。记(L[p],R[p])分别为第(p)块的左右端点,可以发现合法的(l)范围是前缀(R[p])(R[p+1])的最长公共后缀,可以反着建个(SA)求出来,设为(a)。同理合法(r)的范围是后缀(L[p+k])(L[p+k-1])的最长公共前缀,设为(b)(注意与(len-1)(min))。
    (r)的长度需要在([len-a,len-1])之间,所以此时合法的区间个数就是(b-len+a+1)
    复杂度也是(O(nlog^2n))

    **ps: **不知道标算是啥...(好像是SAM+主席树)这是(color{red}{ ext{m}}color{black}{ ext{jt}})的做法。
    这个其实和优秀的拆分差不多...但是考场都忘了啊=-=


    #include <set>
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define rg register
    #define Rev(x) (n-(x)+1)
    //#define gc() getchar()
    #define MAXIN 500000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    typedef long long LL;
    const int N=3e5+5;
    
    int s[N],Log[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    struct Suffix_Array
    {
    	int sa[N],rk[N],sa2[N],tm[N],ht[N],mn[19][N];
    	inline int LCP(int l,int r)
    	{
    		l=rk[l], r=rk[r]; if(l>r) std::swap(l,r);
    		++l; int k=Log[r-l+1];
    		return std::min(mn[k][l],mn[k][r-(1<<k)+1]);
    	}
    	inline int LCP2(int l,int r)
    	{
    		if(l==r) return N;
    		++l; int k=Log[r-l+1];
    		return std::min(mn[k][l],mn[k][r-(1<<k)+1]);
    	}
    	void Build(int *s,int n)
    	{
    		int *x=rk,*y=sa2,m=27;
    		for(int i=0; i<=m; ++i) tm[i]=0;
    		for(int i=1; i<=n; ++i) ++tm[x[i]=s[i]+1];
    		for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
    		for(int i=n; i; --i) sa[tm[x[i]]--]=i;
    		for(int k=1,p=0; k<n; k<<=1,m=p,p=0)
    		{
    			for(int i=n-k+1; i<=n; ++i) y[++p]=i;
    			for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;
    
    			for(int i=0; i<=m; ++i) tm[i]=0;
    			for(int i=1; i<=n; ++i) ++tm[x[i]];
    			for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
    			for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];
    
    			std::swap(x,y), x[sa[1]]=p=1;
    			for(int i=2; i<=n; ++i)
    				x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
    			if(p>=n) break;
    		}
    		for(int i=1; i<=n; ++i) rk[sa[i]]=i;
    		ht[1]=0;
    		for(int i=1,k=0; i<=n; ++i)
    		{
    			if(rk[i]==1) continue;
    			if(k) --k;
    			int p=sa[rk[i]-1];
    			while(i+k<=n && p+k<=n && s[i+k]==s[p+k]) ++k;
    			ht[rk[i]]=k;
    		}
    		for(int i=1; i<=n; ++i) mn[0][i]=ht[i];
    		for(int j=1; j<=Log[n]; ++j)
    			for(int t=1<<j-1,i=n-t; i; --i)
    				mn[j][i]=std::min(mn[j-1][i],mn[j-1][i+t]);
    	}
    }sa1,sa2;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-48,c=gc());
    	return now;
    }
    
    
    int main()
    {
    	freopen("sutoringu.in","r",stdin);
    	freopen("sutoringu.out","w",stdout);
    
    	const int n=read(),K=read();
    	register char c=gc(); while(!isalpha(c)) c=gc(); s[1]=c-'a';
    	for(rg int i=2; i<=n; ++i) s[i]=gc()-'a', Log[i]=Log[i>>1]+1;
    	sa1.Build(s,n), std::reverse(s+1,s+1+n), sa2.Build(s,n);
    	LL ans=0;
    	for(int len=1; len*K<=n; ++len)
    	{
    		std::set<int> st;//直接新开一个 
    		for(int t=K-1,i=1; t--; i+=len) st.insert(sa1.rk[i]);
    		for(int t=(K-1)*len,i=t+1; i+len-1<=n; i+=len)
    		{
    			st.insert(sa1.rk[i]);
    			ans+=sa1.LCP2(*st.begin(),*st.rbegin())>=len;
    			st.erase(sa1.rk[i-t]);
    		}
    		if(len==1) continue;
    		std::set<int> st2;
    		for(int t=K-1,i=len+1; t--; i+=len) st2.insert(sa1.rk[i]);
    		for(int i=K*len+1,j=len; i<=n; i+=len,j+=len)
    		{
    			if(sa1.LCP2(*st2.begin(),*st2.rbegin())>=len)
    			{
    				int a=std::min(sa2.LCP(Rev(j),Rev(j+len)),len-1),b=std::min(sa1.LCP(i,i-len),len-1);//rev!
    				ans+=std::max(0,b-len+a+1);
    			}
    			st2.erase(sa1.rk[j+1]), st2.insert(sa1.rk[i]);
    		}
    	}
    	printf("%lld
    ",ans);
    
    	return 0;
    }
    
  • 相关阅读:
    如何设置SQL Server 全文搜索
    怎么样充分运用ASP.NET 2.0预编译
    怎么样用CSC.exe来编译Visual C#地代码文件
    ASP.NET设计中的性能优化问题
    给Repeater、Datalist和Datagrid增加自动编号列
    ASP.net的RUL重写
    正则表达式学习日记
    SQLCLR(三)触发器
    几个用常用的jscript验证
    Asp.net性能优化总结(二)
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10613052.html
Copyright © 2020-2023  润新知