给n条01信息以及m条01密码,求对于每条密码,有多少条信息与他的最长公共前缀=min(密码长度,该条信息长度)
Sample Input
4 5
3 0 1 0
1 1
3 1 0 0
3 1 1 0
1 0
1 1
2 0 1
5 0 1 0 0 1
2 1 1
INPUT DETAILS:
Four messages; five codewords.
The intercepted messages start with 010, 1, 100, and 110.
The possible codewords start with 0, 1, 01, 01001, and 11.
Sample Output
1
3
1
1
2
//
0 matches only 010:
1 match 1 matches 1, 100, and 110:
01 matches only 010
01001 matches 010:
11 matches 1 and 110
Sol:
对于每个TRIE中的结点进行标记
记下其被经过多少次,记为Longer
再记下对于单词的结束点,标记其次数为Shorter
然后让当前的字符串到TRIE中去跑,走到某个点时,如果当前字符串还没到最后一个字符,
则加上这个结点的Shorter标记(这个点表示单词已走完,但当前字符串没走完,所以是当前字符串的前缀)
否则加上其Longer标记(说有当前字符串已走完,字符仍在延伸,说明当前字符串是其前缀)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define maxn 500500 int n,m,tmp,cnt; int son[maxn][2],a[10100],longer[maxn],shorter[maxn]; void insert() { int p=0; for (int i=1;i<=tmp;i++) { if (!son[p][a[i]]) son[p][a[i]]=++cnt; p=son[p][a[i]]; longer[p]++; //p这个点被经过多少次 } shorter[p]++; //以P为结束端点的单词有多少个 } void work(){ int p=0,ans=0; for (int i=1;i<=tmp;i++) { if (!son[p][a[i]]) break; if (i<tmp) ans+=shorter[son[p][a[i]]]; //如果i<tmp说明当前读的单词还没有走完,此时经过的点 //加上其shorter标记,说是这些单词都是他的前缀 else ans+=longer[son[p][a[i]]]; //否则说是当前读的单词已走完,此时经过的点被经过了 //多少次,就说当前单词是其前缀 p=son[p][a[i]]; } printf("%d ",ans); } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { scanf("%d",&tmp); for (int j=1;j<=tmp;j++) scanf("%d",&a[j]); insert(); } for (int i=1;i<=m;i++) { scanf("%d",&tmp); for (int j=1;j<=tmp;j++) scanf("%d",&a[j]); work(); } return 0; }