深信服2020研发新员工训练题【组词】(字典树单串多词判断)

题目描述
判断所给的字符串是否由所给的词典中的若干个词组成。

如已知词典[“code”, “sangfor”, “org”]
则字符串"codesangfororg" 由上述词典组成,
字符串"codesangforsangfororg" 也由上述词典组成,
但字符串"sangforcom" 则不由上述词典组成。

输入描述:
第一行一个数字K 表示词典个数
后面若干行则为具体的输入词典,一个词典一行
最后一行输入待判定的字符串
输出描述:
若字符串为对应的词典组成,则输出yes,否则输出no
示例1
输入
3
code
sangfor
org
codesangfororg

输出
yes

示例2
输入
0
code

输出
no

看到单串多词存在性的判断以为是AC自动机,后来发现题目要求字典里的单词强制存在不允许失配。那感觉就可以用字典树搞定了,因为失配直接被判定为no,所以是在一个长串上不断匹配已有单词,重要的是判断长串匹配过程中单个单词是匹配完成还是匹配失败。建树过程中记录每个单词的结尾,匹配过程中若失配了,判断上一位字符是否是结尾【即失配情况只有可能是匹配完了一个单词,开始下一个单词】若是结尾,那么匹配指针从当前初始化到树根,从头匹配,否则直接判定no。

#include 
#include 
using namespace std;
int tot;
int trie[500000][26];
int num[500000];///记录节点被访问次数
int insert(char *str,int rt)//建树
{
    for(int j=0; str[j]; j++)
    {
        int x=str[j]-'a';//第x个分支是这个字母,此层的分支就是x,从x再分出节点
        if(trie[rt][x]==0)
        {
            trie[rt][x]=++tot;//若是未标记的新字母,则重新标号
        }
        rt=trie[rt][x];
    }
    num[rt]=1;
}
bool find(char *str,int floor)//查询字典
{
    int rt=floor;
    int x,len=strlen(str);
    for(int i=0; i<len; i++)
    {
        x=str[i]-'a';
        if(trie[rt][x]==0&&num[rt]!=1)
            return false;
        if(trie[rt][x]==0&&num[rt]==1)
        {
            rt=floor;
            i--;
            continue;
        }
        rt=trie[rt][x];
    }
    if(num[rt]==0) return false;
    return true;
}

char str[100];
int main()
{
    tot=0;///根节点
    int floor=++tot;
    memset(trie[floor],0,sizeof(trie[floor]));
    memset(num,0,sizeof(num));
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%s",str);
        insert(str,floor);
    }
    char s[15000];
    scanf("%s",s);
    printf("%s\n",find(s,floor)?"yes":"no");
    return 0;
}

你可能感兴趣的:(字典树)