SPOJ 8222 Substrings(后缀自动机)

【题目链接】 http://www.spoj.com/problems/NSUBSTR/

【题目大意】

  给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值。

  求出所有的F。

【题解】

  在SAM中,一个串出现的次数就是|Right(s)|,我们按长度从小到大分配内存单位,
  从后往前计算可以获得Right值大小,用所有的Right去更新相应长度的答案即可。

【代码】

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=500005;
char s[N];
struct sam{
	  int p,q,np,nq,cnt,last,a[N][26],l[N],f[N];
	  sam(){cnt=0;last=++cnt;}
	  void extend(int c){
		    p=last;np=last=++cnt;l[np]=l[p]+1;
		    while(!a[p][c]&&p)a[p][c]=np,p=f[p];
		    if(!p)f[np]=1;
		    else{
			      q=a[p][c];
			      if(l[p]+1==l[q])f[np]=q;
			      else{
				        nq=++cnt;l[nq]=l[p]+1;
				        memcpy(a[nq],a[q],sizeof(a[q]));
				        f[nq]=f[q]; f[np]=f[q]=nq;
				        while(a[p][c]==q)a[p][c]=nq,p=f[p];
			      }
		    }
	  }int b[N],x[N],r[N];
	  void build(){
		    scanf("%s",s+1);
		    int len=strlen(s+1);
		    for(int i=1;i<=len;i++)extend(s[i]-‘a‘);
		    for(int i=1;i<=cnt;i++)b[l[i]]++;
		    for(int i=1;i<=len;i++)b[i]+=b[i-1];
		    for(int i=1;i<=cnt;i++)x[b[l[i]]--]=i;
		    for(int i=p=1;i<=len;i++){p=a[p][s[i]-‘a‘];r[p]++;}
		    for(int i=cnt;i;i--)r[f[x[i]]]+=r[x[i]];
	  }
	  int F[N];
	  void CalF(){
	  	  int len=strlen(s+1);
	      for(int i=1;i<=cnt;i++)F[l[i]]=max(F[l[i]],r[i]);
	      for(int i=1;i<=len;i++)printf("%d\n",F[i]);
	  }
}sam;
int main(){
    sam.build();
    sam.CalF();
    return 0;
}

  

时间: 2024-10-24 20:22:30

SPOJ 8222 Substrings(后缀自动机)的相关文章

【CF316G3】Good Substrings 后缀自动机

[CF316G3]Good Substrings 题意:给出n个限制(p,l,r),我们称一个字符串满足一个限制当且仅当这个字符串在p中的出现次数在[l,r]之间.现在想问你S的所有本质不同的子串中,有多少个满足所有限制. |S|,|p|<=10^5,n<=10. 题解:比较简单的后缀自动机题,我们先把原串和所有限制串放到一起建一个广义后缀自动机,然后在pre树上统计一下即可得到每个子串在每个限制串中出现了多少次.现在我们想知道原串中有多少满足条件的子串,即我们统计一下所有出现次数符合要求的,

Lexicographical Substring Search SPOJ - SUBLEX (后缀自动机)

Lexicographical Substrings Search \[ Time Limit: 149 ms \quad Memory Limit: 1572864 kB \] 题意 给出一个字符串,求出这个字符串上字典序第 \(k\) 小的子串. 思路 先对给出的字符串构建后缀自动机,因为后缀自动机上从根节点走到任意节点都是原串的一个子串,所以我们可以 \(dfs\) 求出节点 \(i\) 往后存在多少个子串. 对于查询第 \(k\) 小的子串时,在用一个 \(dfs\) 来求,对于当前节点

【POJ3415】Common Substrings 后缀自动机

转载请注明出处:http://blog.csdn.net/vmurder/article/details/42710069 其实我就是觉得原创的访问量比未授权盗版多有点不爽233... 题意: 给两个串,问有多少长度大于等于K的公共子串(位置不同也算一对) 题解: 后缀自动机DP 对第一个串建立后缀自动机,然后做一些预处理, 然后拿第二个串在后缀自动机上跑,到每个节点加一次贡献. 但是这样需要每个点往parent树上跑一遍,会TLE,所以可以加个lazy. 然后代码中有两次运用到拓扑序来从子向父

SPOJ8222 Substrings( 后缀自动机 + dp )

题目大意:给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值.F(1)..F(Length(S)) 建出SAM, 然后求出Right, 求Right可以按拓扑序dp..Right就是某个点到结束状态的路径数, parent树上last的那一条链都是结束状态...然后用Right去更新答案.. spoj卡常数..一开始用DFS就炸了, 改用BFS就A了.. (贴一下丽洁姐的题解: 我们构造S的SAM,那么对于一个节点s,它的长度范围是[Min(s),Max(s)],同时他的出

spoj Distinct Substrings 后缀数组

给定一个字符串,求不相同的子串的个数. 假如给字符串“ABA";排列的子串可能: A B A AB  BA ABA 共3*(3+1)/2=6种; 后缀数组表示时: A ABA BA 对于A和AB height[i]=1; 表明一个长度公共,所以ABA中多出现了A这个子串,所以6-1=5: 对于ABA BA height[i]=0,所以不需要减去. 最后答案为5: #include<iostream> #include<stdio.h> #include<string

SPOJ 1812 LCS2 [后缀自动机]

题意: 求多个串<=10的最长连续子串 一个串建SAM,然后其他串在上面走 每个状态记录所有串在这个状态的公共子串的最小值 一个串在上面走的时候记录与每个状态公共子串的最大值,注意出现次数向父亲传递,一个状态能到达说明了Suffix Link指向的状态可以取到最大子串,这一步对val后基数排序然后倒着更新就行了 注意两点: 1.空间要开两倍,基数排序用的东西也要两倍哦 2.答案不能用mn[1]更新!!!!因为mn[1]没有意义啊root状态一个子串也没有还可能有某些bug #include <

[SPOJ8222]NSUBSTR - Substrings 后缀自动机

1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int sz=0,la,rt; 6 int ch[500010][26],l[500010],fa[500010]; 7 void Extend(int c){ 8 int end=++sz,tmp=la; 9 l[end]=l[tmp]+1; 10 while(tmp&&!ch[tm

SPOJ - SUBLEX 【后缀自动机】

题目 求第K小子串 题解 建好SAM后,拓扑排序,反向传递后面所形成的串的数量 最后从根开始,按照儿子形成串的数量与k比较走就好了 #include<iostream> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long int #define REP(i,n) for (int i = 1; i <= (n)

SPOJ8222 NSUBSTR - Substrings 后缀自动机_动态规划

讲起来不是特别好讲.总之,如果 $dp[i+1]>=dp[i]$,故$dp[i]=max(dp[i],dp[i+1])$ Code: #include <cstdio> #include <algorithm> #include <cstring> #define setIO(s) freopen(s".in","r",stdin) #define maxn 2000000 #define N 30 #define ll l