[Usaco2008 Dec]Secret Message 秘密信息

给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;
}
原文地址:https://www.cnblogs.com/cutemush/p/12411837.html