zoukankan      html  css  js  c++  java
  • Luogu4341/BZOJ2251 [BJWC2010]外星联络

    题目传送门

    算法分析

    \(n\le 3000\),即\(n^2\)的时空复杂度都是可以接受的,我们考虑直接将所有的后缀插入一颗Trie,那么这颗Trie的所有前缀即为这个字符串的所有子串,在Trie上跑前缀和即可统计每一个子串出现了多少次。因为Triedfs遍历就是按字典序排序后的结果,所以我们直接跑一边dfs,如果某个子串出现次数\(\ge 2\),则输出这个字串。


    我们换个想法:处理子串问题最常用的算法是什么?(我会SAM)后缀数组!

    考虑任意两个后缀的前缀,这个前缀一定是1个出现过至少2次的子串,即我们题目中要求的东西。

    下面问题就在于:如何不重不漏地把这样的子串求出来。

    首先有一点不难证明:当\(i\)为一个定值,\(k\)为自变量,\(\forall k1,k2\in[i,n],k1<k2,LCP(i,k1)\ge LCP(i,k2)\)

    那么对于某一个后缀\(i\),设子串长度为\(j\),按排名枚举在\(i\)之后的每一个后缀(设当前枚举到的后缀为\(k\)),如果\(height(k)\ge j\),说明\(LCP(i,k)\ge j\),即该子串也在后缀\(k\)中出现。而且我们可以发现,对于每一个长度\(j\),如果比它更大的可行,那么它一定可行。所以我们可以从大到小枚举\(j\),为了防止出现重复统计,\(j\ge height(i)\),这样我们可以保证得到的子串一定是不重不漏的,并且按照这个顺序得到的答案恰好是与字典序相反的(因为有相同前缀的子串是按照长度从大到小的顺序枚举的)。

    代码实现

    #include<bits/stdc++.h>//Trie版
    using namespace std;
    #define maxn 3005
    #define maxm 10000005
    #define inf 0x3f3f3f3f
    #define LL long long
    #define mod 1000000007
    #define local
    template <typename Tp>
    void read(Tp &x){
    	x=0;int fh=1;char c=getchar();
    	while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
    	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
    }
    int n;
    int a[maxn];
    int ch[maxm][2],siz[maxm],cnt=1;
    void ins(int id){
    	int p=1,dir;
    	for(int i=id;i<=n;i++){
    		dir=a[i];
    		if(!ch[p][dir])ch[p][dir]=++cnt;
    		p=ch[p][dir];siz[p]++;
    	}
    }
    void out(int p){
    	if(siz[p]>1)printf("%d\n",siz[p]);
    	if(ch[p][0])out(ch[p][0]);
    	if(ch[p][1])out(ch[p][1]);
    }
    int main(){
    	read(n);
    	for(int i=1;i<=n;i++)scanf("%1d",&a[i]);
    	for(int i=1;i<=n;i++)ins(i);
    	out(1);
    	return 0;
    }
    

    后缀数组版(找了一篇题解)

  • 相关阅读:
    一道编程题: 在1~n之间选择若干个数,使其和为m
    关于raft算法
    程序员算法基础——动态规划
    c++中两个类互相引用的问题
    c++ 之模板进阶
    jmeter分布式操作-远程启动功能探索
    linux下安装不同版本的jdk
    Jmeter插件监控服务器性能
    测试开发面试-技术持续累积
    python:Jpype安装和使用
  • 原文地址:https://www.cnblogs.com/ZigZagKmp/p/11487587.html
Copyright © 2011-2022 走看看