破译密码
Description
大意就是:给n条01信息以及m条01密码,
求对于每条密码,
有多少条信息与他的最长公共前缀=min(密码长度,该条信息长度),
1<=N,M<=50000,其长度均小于等于10000
输入的总长度<=500000
Input
第1行输入N和M
之后N行描述秘密信息,
之后M行描述密码.
每行先输入一个整数表示信息或密码的长度,之后输入这个信息或密码.所有数字之间都用空格隔开.
Output
共M行,输出每条密码的匹配信息数.
Sample Input
4 5 //4条信息,5组密码
3 0 1 0 //第一条信息,长度为3,信息为010
1 1
3 1 0 0
3 1 1 0
1 0 //第一条密码,长度为1,密码为0
1 1
2 0 1
5 0 1 0 0 1
2 1 1
Sample Output
1 //0是010的前缀
3 //1是1,100,110的前缀
1 //01是010的前缀
1 //010是01001的前缀
2 //1是11的前缀,11是110的前缀
sol:先将读入的信息串建立Trie树,本题信息可能是密码的前缀,密码也可能是信息的前缀。难点是怎样判断对于某个串,别人是它的前缀,它又是别人的前缀呢?如:
我们对每个点进行标记:
longer[i],记录第i个结点,有多少个单词经过了它,即它被经过了多少次。
shorter[i],记录以第i个结点为结束位置的的单词数。
在进行查找时,依次查找密码的每一个字符,若当前字符不是最后一个字符,则ans+shorter[i](某些信息是该密码的前缀),否则ans+longer[i](该密码查找结束,该密码是信息的前缀)。
代码如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define maxn 500500 6 int n,m,tmp,cnt; 7 int son[maxn][2],a[10100],longer[maxn],shorter[maxn]; 8 9 void insert() 10 { 11 int p=0; 12 for (int i=1;i<=tmp;i++) 13 { 14 if (!son[p][a[i]]) 15 son[p][a[i]]=++cnt; 16 p=son[p][a[i]]; 17 longer[p]++; 18 //p这个点被经过多少次 19 } 20 shorter[p]++; 21 //以P为结束端点的单词有多少个 22 } 23 24 void work(){ 25 int p=0,ans=0; 26 for (int i=1;i<=tmp;i++) 27 { 28 if (!son[p][a[i]]) 29 break; 30 if (i<tmp) 31 ans+=shorter[son[p][a[i]]]; 32 //如果i<tmp说明当前读的单词还没有走完,此时经过的点 33 //加上其shorter标记,说是这些单词都是他的前缀 34 else 35 ans+=longer[son[p][a[i]]]; 36 //否则说是当前读的单词已走完,此时经过的点被经过了 37 //多少次,就说当前单词是其前缀 38 p=son[p][a[i]]; 39 } 40 printf("%d\n",ans); 41 } 42 43 int main(){ 44 scanf("%d%d",&n,&m); 45 for (int i=1;i<=n;i++) 46 { 47 scanf("%d",&tmp); 48 for (int j=1;j<=tmp;j++) 49 scanf("%d",&a[j]); 50 insert(); 51 } 52 for (int i=1;i<=m;i++) 53 { 54 scanf("%d",&tmp); 55 for (int j=1;j<=tmp;j++) 56 scanf("%d",&a[j]); 57 work(); 58 } 59 return 0; 60 }