子串统计

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 }

 

以上是关于子串统计的主要内容,如果未能解决你的问题,请参考以下文章

子串统计

c_cpp 快速代码片段,用于在统计(阻止)/ dev / rdsk中的设备时验证fstat64和stat64的行为。

LeetCode 0828. 统计子串中的唯一字符

[spoj DISUBSTR]后缀数组统计不同子串个数

C中统计子串个数

两个字符串的最长公共子串python代码