• 题解「CF204E Little Elephant and Strings」


    后缀数组+ST表+分块


    合法的子串必须满足至少是k个串的字串。这个要求让我们自然而然想到一道相似的题 P5546 [POI2000]公共串 ,这道题用后缀数组很容易想到解法。

    于是开始后缀数组乱搞。

    先将所有字符串用分隔符隔开连接成一个串。注意,这里的分隔符必须两两不同,否则求出的 (height) 数组将会不正确。

    在所得串上跑一遍后缀排序,得到 (height) 数组。

    然后我们使用双指针在 (height) 数组上来枚举合法的区间,左右端点的扩展类似于莫队,当区间内出现了 (K) 个原串的字串时,此区间合法。

    用ST表求出区间内 (height) 的最小值 (t),即为区间内所有后缀的最长公共前缀,此时区间内所有后缀的长度为 (t) 的前缀均为合法字串。

    不难发现一个性质:若后缀的长度为 (t) 的前缀合法,则该后缀长度为 (t' leq t) 的前缀均合法。也就是说只需要记录每个后缀最长的合法前缀的长度即可。

    需要支持区间对一个值取max,并单点查询。

    因为 懒得想其他方法 分块好写,所以这里就直接上分块了。


    ( ext{Code}:)

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #define maxn 200005
    #define R register
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long lxl;
    
    inline int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    char s[maxn];
    int N,M,K,a[maxn],n;
    lxl ans[maxn];
    int SA[maxn],rnk[maxn],tax[maxn],tp[maxn],ht[maxn],belong[maxn];
    
    inline void Qsort()
    {
    	for(int i=0;i<=M;++i) tax[i]=0;
    	for(int i=1;i<=N;++i) ++tax[rnk[i]];
    	for(int i=1;i<=M;++i) tax[i]+=tax[i-1];
    	for(int i=N;i>=1;--i) SA[tax[rnk[tp[i]]]--]=tp[i];
    }
    
    inline void SuffixSort()
    {
    	M=30+n;
    	for(int i=1;i<=N;++i)
    		rnk[i]=a[i],tp[i]=i;
    	Qsort();
    	for(int w=1,p=0;p<N;M=p,w<<=1)
    	{
    		p=0;
    		for(int i=1;i<=w;++i) tp[++p]=N-w+i;
    		for(int i=1;i<=N;++i) if(SA[i]>w) tp[++p]=SA[i]-w;
    		Qsort();
    		swap(tp,rnk);
    		rnk[SA[1]]=p=1;
    		for(int i=2;i<=N;++i)
    			rnk[SA[i]]=(tp[SA[i]]==tp[SA[i-1]]&&tp[SA[i]+w]==tp[SA[i-1]+w]) ? p : ++p;
    	}
    	for(int i=1,k=0;i<=N;++i)
    	{
    		if(k) --k;
    		while(a[i+k]==a[SA[rnk[i]-1]+k]) ++k;
    		ht[rnk[i]]=k;
    	}
    }
    
    struct ST_Table
    {
    	int d[maxn][30],lg[maxn];
    	inline void init()
    	{
    		lg[0]=-1;
    		for(int i=1;i<=N;++i) d[i][0]=ht[i],lg[i]=lg[i>>1]+1;
    		for(int j=1;j<=25;++j)
    			for(int i=1;i+(1<<(j-1))<=N;++i)
    				d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
    	}
    	inline int query(int l,int r)
    	{
    		int k=lg[r-l+1];
    		return min(d[l][k],d[r-(1<<k)+1][k]);
    	}
    }st;
    
    struct BIG_Block// 大分块 (雾)
    {
    	#define BN 400
    	lxl a[maxn],tag[maxn/BN];
    	inline int pos(int x) {return (x-1)/BN+1;}
    	inline void Get_Max(int l,int r,lxl d)
    	{
    		int bl=pos(l),br=pos(r);
    		for(int i=bl+1;i<br;++i)
    			tag[i]=max(tag[i],d);
    		if(bl==br)
    			for(int i=l;i<=r;++i)
    				a[i]=max(a[i],d);
    		else
    		{
    			for(int i=l;i<=bl*BN;++i)
    				a[i]=max(a[i],d);
    			for(int i=(br-1)*BN+1;i<=r;++i)
    				a[i]=max(a[i],d);
    		}
    	}
    	inline void Get_Val(int x)
    	{
    		a[x]=max(max(a[x],tag[pos(x)]),min(a[x-1],1ll*ht[x]));
    	}
    }_9baka;
    
    int cnt[maxn],flag;
    
    inline void Add(int i)
    {
    	if(!belong[i]) return;
    	if(!cnt[belong[i]]) ++flag;
    	++cnt[belong[i]];
    }
    
    inline void Del(int i)
    {
    	if(!belong[i]) return;
    	--cnt[belong[i]];
    	if(!cnt[belong[i]]) --flag;
    }
    
    int main()
    {
    	// freopen("CF204E.in","r",stdin);
    	n=read(),K=read();
    	if(K==1)
    	{
    		for(int i=1;i<=n;++i)
    		{
    			scanf(" %s",s+1);
    			int len=strlen(s+1);
    			printf("%lld ",1ll*(1+len)*len>>1);
    		}
    		return 0;
    	}
    	for(int i=1;i<=n;++i)
    	{
    		scanf(" %s",s+1);
    		int len=strlen(s+1);
    		for(int j=1;j<=len;++j)
    			a[++N]=s[j]-'a'+1,belong[N]=i;
    		a[++N]=26+i;
    	}
    	SuffixSort();
    	st.init();
    	for(int i=1,j=0;i<=N;++i)
    	{
    		while(j<=N&&flag<K) Add(SA[++j])
            // 为了使 t 尽量大,这里只枚举出最短的合法区间
    		if(flag<K) break;
            // 如果右端点枚举到边界都不合法,则左端点再缩小还是不合法,直接break
    		int t=st.query(i+1,j);
    		_9baka.Get_Max(i,j,t);
    		Del(SA[i]);
    	}
    	for(int i=2;i<=N;++i)
    		_9baka.Get_Val(i);
            //若前后两后缀有公共前缀,则可能前面的后缀更新后面的后缀的答案
    	for(int i=1;i<=N;++i)
    		ans[belong[SA[i]]]+=_9baka.a[i];
            // 最长合法前缀的长度即为合法前缀的个数
    	for(int i=1;i<=n;++i)
    		printf("%lld ",ans[i]);
    	return 0;
    }
    
    
  • 相关阅读:
    Eclipse 软件 Java 解决:出现的editor does not contain a main type错误框 问题
    Oracle SQL高级编程——分析函数(窗口函数)全面讲解
    瑞联科技:Pwp3框架 调用存储过程返还数据集合 到前端界面展示
    spring jdbctemplate调用存储过程,返回list对象
    利用jquery操作Radio方法小结
    Oracle临时表
    JdbcUtil
    整理oracle 树形查询
    Js:消息弹出框、获取时间区间、时间格式、easyui datebox 自定义校验、表单数据转化json、控制两个日期不能只填一个
    Java对象之间的深度复制拷贝
  • 原文地址:https://www.cnblogs.com/syc233/p/13616217.html
Copyright © 2020-2023  润新知