zoukankan      html  css  js  c++  java
  • NOI2016 优秀的拆分

    优秀的拆分

    如果一个字符串可以被拆分为 ( ext{AABB}) 的形式,其中 ( ext{A})( ext{B}) 是任意非空字符串,则我们称该字符串的这种拆分是优秀的。
    例如,对于字符串 $ exttt{aabaabaa} $ ,如果令 ( ext{A}= exttt{aab})( ext{B}= exttt{a}),我们就找到了这个字符串拆分成 ( ext{AABB}) 的一种方式。

    一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。
    比如我们令 ( ext{A}= exttt{a})( ext{B}= exttt{baa}),也可以用 ( ext{AABB}) 表示出上述字符串;但是,字符串 ( exttt{abaabaa}) 就没有优秀的拆分。

    现在给出一个长度为 (n) 的字符串 (S),我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

    以下事项需要注意:

    1. 出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
    2. 在一个拆分中,允许出现 ( ext{A}= ext{B})。例如 ( exttt{cccc}) 存在拆分 ( ext{A}= ext{B}= exttt{c})
    3. 字符串本身也是它的一个子串。

    对于全部的测试点,(1 leq T leq 10, n leq 30000)

    题解

    等价于对每个 (i) 求出 (Pre_i)(Suf_i),表示 (S[1 : i]) 的 AA 型后缀个数以及 (S[i : |S|]) 的 AA 型前缀个数,那么答案显然就是 (sum^n_{i=1} Pre_iSuf_{i+1})

    枚举一下 |A|,把 S 按 |A| 切段,对每两段去讨论一下,可以发现只要求 lcp 就行了

    https://blog.csdn.net/qq_40512553/article/details/78915344

    时间复杂度 (O(n ln n log n))

    CO int N=3e4+10;
    CO uint128 B=131;
    uint128 pw[N],val[N];
    char str[N];
    int pre[N],suf[N];
    
    IN uint128 calc(int l,int r){
    	return val[r]-val[l-1]*pw[r-l+1];
    }
    void real_main(){
    	scanf("%s",str+1);
    	int n=strlen(str+1);
    	for(int i=1;i<=n;++i) val[i]=val[i-1]*B+str[i];
    	fill(pre+1,pre+n+1,0),fill(suf+1,suf+n+1,0);
    	for(int L=1;2*L<=n;++L)for(int i=L+L;i<=n;i+=L){
    		int p=i-L;
    		if(str[i]!=str[p]) continue;
    		int lcs=0;
    		{
    			int l=1,r=L;
    			while(l<r){
    				int mid=(l+r+1)>>1;
    				if(calc(p-mid+1,p)==calc(i-mid+1,i)) l=mid;
    				else r=mid-1;
    			}
    			lcs=l;
    		}
    		int lcp=0;
    		{
    			int l=1,r=min(L,n-i+1);
    			while(l<r){
    				int mid=(l+r+1)>>1;
    				if(calc(p,p+mid-1)==calc(i,i+mid-1)) l=mid;
    				else r=mid-1;
    			}
    			lcp=l;
    		}
    		if(lcs+lcp>L){
    			++pre[i-lcs+L],--pre[i+lcp];
    			++suf[p-lcs+1],--suf[p+lcp-L+1];
    		}
    	}
    	for(int i=1;i<=n;++i) pre[i]+=pre[i-1],suf[i]+=suf[i-1];
    	int64 ans=0;
    	for(int i=1;i<n;++i) ans+=(int64)pre[i]*suf[i+1];
    	printf("%lld
    ",ans);
    }
    int main(){
    	pw[0]=1;
    	for(int i=1;i<N;++i) pw[i]=pw[i-1]*B;
    	for(int T=read<int>();T--;) real_main();
    	return 0;
    }
    

    unsigned long long会被卡,最后一个测试点的最后一组数据就是专门卡它的。

  • 相关阅读:
    leetcode------Remove Element
    leetcode------Merge Two Sorted Lists
    [转载]Unity3D 游戏引擎之使用C#语言建立本地数据库(SQLITE)
    [转载]VS2012创建MVC3项目提示错误: 此模板尝试加载组件程序集 “NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”。
    [转载]Unity3D 访问Access数据库
    [转载]C#导入XLS数据到数据库
    [转载]Unity3D的断点调试功能
    [Unity3D]支持的视频格式
    unity 3d 获取鼠标当前坐标
    [转载]VS2012程序打包部署详解
  • 原文地址:https://www.cnblogs.com/autoint/p/12337659.html
Copyright © 2011-2022 走看看