子串统计
Posted cutepota
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了子串统计相关的知识,希望对你有一定的参考价值。
Description
给你一个长度为N的01字符串,然后问你在它的所有子串中,那些出现次数大于1次的子串的出现次数。输出的顺序按子串本身的字典序,例如假设01这个子串出现3次,011这个子串出现2次,则先输出3再输出2.
Input
输入文件的第一行是一个整数N ,代表串的长度。
输入文件第二行包含一个长度为N 的 01 串,信号串。
0 <= N <=3000
Output
输出文件的每一行包含一个出现次数大于1 的子串所出现的次数。输出的顺序按对应的子串的字典序排列
Sample Input
5
00101
Sample Output
3 //0出现3次
2 //1出现2次
2 //01出现2次
sol:本题一要取出所有子串,二要查询子串出现的次数,三要按字典序输出。这里写出用Trie来解决的方法。
1.取出所有子串:暴力的做法是枚举一个开始点i,枚举一个结束点j,取出i..j的字符。这里0 <= N <=3000,用暴力做法也不会超时,但我们还要查找这些子串出现的次数。这里可以将给出的01串的所有后缀取出来,存入字典树。对于样例数据00101,如下所示:
2.查找每个前缀出现的次数:从根结点出发,依次取出每个后缀字符串的所有前缀,判断其出现的次数是否大于1.
如第1个后缀子串00101,取5次依次得到下面的前缀:
取1位:0 //(1,1)我们取到了从第1位开始的一个字符
取2位:00 //(1,2)
取3位:001 //(1,3)
取4位:0010 //(1,4)
取5位:00101 //(1,5)
取第2个后缀子串的前缀:
取1位:0 //(2,1)我们取到了从第2位开始的一个字符
取2位:01 //(2,2)
取3位:010 //(2,3)
取4位:0101 //(2,4)
......
小结:我们将字符串的所有后缀取出,存入Trie,当然在加入Trie的时候,要记录下每个结点出现的次数。在遍历的时候,从根出发,所走到的每一个点,这个路径上经过的字符,就构成从前字符串的某个子串。因为,用后缀相当于固定了待取子串的开始点,从根结点依次取前缀时,相当于固定了子串的结束点。
最后,要保证按字典序输出,即走到某结点时,它向左边走有结点时,先走左边,否则走右边。
代码实现:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 void read(int &x){ 5 x=0;char ch=1;int fh; 6 while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar(); 7 if(ch==‘-‘) fh=-1,ch=getchar(); 8 else fh=1; 9 while(ch>=‘0‘&&ch<=‘9‘){ 10 x=(x<<1)+(x<<3)+ch-‘0‘; 11 ch=getchar(); 12 } 13 x*=fh; 14 } 15 16 char s[3007]; 17 int ch[10000000][2],tot,root; 18 19 int n,cnt[10000000]; 20 21 22 int chk(char s) 23 { 24 return s-‘0‘; 25 } 26 27 void insert(int pla) 28 { 29 int p=root; 30 for(register int i=pla;i<=n;i++) 31 { 32 int k=chk(s[i]); 33 if(!ch[p][k]) 34 ch[p][k]=++tot; 35 p=ch[p][k]; 36 ++cnt[p]; 37 } 38 } 39 40 void dfs(int x) 41 { 42 if(cnt[x]>1&&x!=root) 43 //如果不为根结点,则出现次数大于1,输出其出现次数 44 cout<<cnt[x]<<endl; 45 if(ch[x][0]) //如果有左结点0 46 dfs(ch[x][0]); 47 if(ch[x][1]) //如果有右结点1 48 dfs(ch[x][1]); 49 } 50 51 int main(){ 52 53 54 cin>>n; 55 cin>>(s+1); 56 root=tot=0; 57 for(register int i=n;i>=1;i--) 58 //将其所有后缀加入到TRIE中 59 { 60 insert(i); 61 } 62 dfs(root); 63 return 0; 64 }
以上是关于子串统计的主要内容,如果未能解决你的问题,请参考以下文章