用记录附加信息的val数组记录次数即可。
trie的原理:每个可能出现的字目给一个编号c,那么整个树就是一个c叉树
ch[u][c]表示 节点u走c边过去之后的节点
PS:trie树还有种动态写法,使用指针和动态分配内存代替了连续的ch数组,更加节省内存。
Reference:http://blog.csdn.net/architect19/article/details/8966247
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define maxnode 400010
#define sigma_size 22 char a[maxnode][sigma_size];
int t[maxnode];
int n,m;
//struct Trie
//{
int ch[maxnode][sigma_size]; //ch[i][j]:记录结点i的那个编号为j的子节点
int val[maxnode]; //val[i]:i节点的附加信息,
//若val[i]=0不是还没到单词结束。否则val[i]为该单词的出现次数
int sz;
void Trie()
{
sz=;
memset(ch,,sizeof(ch));
memset(val,,sizeof(val));
memset(t,,sizeof(t));
}
int idx(char c) //idx(c)即字符c的编号。
{
//A G C T
if (c=='A') return ;
if (c=='G') return ;
if (c=='C') return ;
if (c=='T') return ;
//return c-'A'; //第一个字符是A
}
void Insert(char s[sigma_size],int v)
{
int u=,n=strlen(s);
for (int i=;i<n;i++)
{
int c=idx(s[i]);
if (!ch[u][c])
{
memset(ch[sz],,sizeof(ch[sz]));
val[sz]=;
ch[u][c]=sz++;
}
u=ch[u][c];
}
//val[u]=v;
val[u]+=v;
}
int Query(char s[sigma_size]) //times of s
{
int u=,c;
int tm=strlen(s);
for (int i=;i<tm;i++)
{
//c=s[i]-'A';
c=idx(s[i]);
if (!ch[u][c]) return ;
u=ch[u][c];
//if (val[u]==1) return true; //若此时s还没走完但trie树上已经走到结尾了,即树上单词是s的前缀
}
return val[u];
}
//}; int main()
{
//freopen("in.txt","r",stdin);
//ios::sync_with_stdio(false); //while (cin>>n>>m)
while(~scanf("%d%d",&n,&m))
{
Trie();
if ((n!=)&&(m!=))
{
for (int i=;i<=n;i++)
{
//cin>>a[i];
scanf("%s",a[i]);
Insert(a[i],);
}
for (int i=;i<=n;i++)
{
int tm=Query(a[i]);
t[tm]++;
}
for (int i=;i<=n;i++)
printf("%d\n",t[i]/i);
//cout<<endl;
}
else break;
} return ;
}