Trie based string match

最近经常遇到WinEdt dvi->pdf文件过程出现 error unable to open “*.pdf” 的错误,然后我一般都是打开资源管理器,然后找到这个文件对应的句柄,然后把对应的process kill掉,
不知道大家有没有更好的解决方案。

之前这道题目一直掐在这里,当时是偶然看到vjudge这个融合几个OJ的一个神奇OJ,然后一个HUST的学弟给我发的链接。

但是第一道题目发现比Trie的裸题稍微加了点东西,之前有一个大致的朴素算法也是每个后缀去match这个字典树,但是当时好像是发现这个做法对于是否能够处理keyword之间有前缀关系
产生了怀疑还是什么,然后就停在那里,没有继续做了。

后来发给瞿神,瞿神也是这个思路,但是没A。

最后还是在光环无限的fawkes大神的指导下,A了这道题目。首先答案是0~N的范围,N是keyword数这点和题意是契合的,但是fawes大神考虑到keyword是否可能出现重复,于是就要稍微改改代码了。果然在大神指导下A了这道题目。

附上代码:里面有几个地方的改进是在大神指导下完成的。

  1. 首先传后缀只要传入一个指针,这样的话就需要转成char 传进去,但是c_str()是const char 要注意。

  2. 然后之前有个bug就是遍历trie的指针,和query 字符串的遍历都要考虑是否越界,我当时以为肯定trie先遍历完,于是query不会越界,但是后来发现其实字符串如果trie里有一个keyword是query的前缀,
    那么就会出现字符串先越界,所以要特别当心,这个还是代码经验不够丰富导致的,我一眼还看不出bug,超珲立马就看出来了。

  3. 然后就是判断字符串是否越界,用了strlen,但是大神强调这是O(n),用判断是否\0处理,同时当时写的快了,漏了一个bool 非来判断字符串遍历结束。

  4. 如果是处理keyword重复的话,就是每个count含义和之前自己定义的不同了,之前是为了计算匹配了前缀的keyword数方便,在构建trie的时候,就每次往下一个keyword当前count就
    ++,计算的是子树里keyword的个数。现在是当前keyword在字典里有几个。于是再每次遍历完一个keyword的时候,就将其count++就可以了,两个for循环只要第一个for结束之后count++就可以了,表示最后一个节点的count++,正好对应了keyword的count。

5.还有一个细节也是代码不熟练,我一直纠结如何避免重复的匹配,以为题目keyword只有是否出现的概念,多个keyword也是如此,超珲大神用isword=false来加锁,这样第一次count累加了之后之后再match就不会多累加了。然后ans全局变量作为累加值,其实主要是这个加锁的思想没有立马反应过来。

按照瞿神的说法,这道题目的数据不够强,如果强的话,需要AC自动机,虽然我还不会。。。

http://vjudge.net/contest/view.action?cid=50652#problem/F

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
using namespace std;
struct node
{
    bool isword;
    node* child[26];
    int count;
    node()
    {
        for(int i=0;i<26;i++)
            child[i]=NULL;
        isword=false;
        count=0;
    }
};

node root;
string dict[10000];
int n,m;
string query;

void CreateTrie()
{
    root.isword=false;
    for(int i=0;i<26;i++)
        root.child[i]=NULL;
    for(int i=0;i<n;i++)
    {
        node* p=&root;
        for(int j=0;j<dict[i].size();j++)
        {
            node*& pchild=p->child[dict[i][j]-'a'];
            if(pchild)
            {
                //pchild->count++;
                p=pchild;
            }
            else
            {
                pchild=new node;
                pchild->isword=false;
                //pchild->count=1;
                p=pchild;
            }
        }
        p->isword=true;
        p->count++;
    }

}
/*
int GetCount()
{
    for(int start=0;start<query)
    node* p=&root;
    for(int i=0;i<query.size();i++)
    {
        if(p->child[query[i]-'a']!=NULL)
            p=p->child[query[i]-'a'];
        else
            return 0;
    }
    return p->count;
}
*/
//bool keyindesc[n];//initaial false;
int ans;
void find(const char* query)
{
    node* p=&root;
    int stri=0;
    do
    {
        if(!query[stri])
            break;
        p=p->child[query[stri++]-'a'];
        if(p && p->isword)
        {
            p->isword=false;
            ans+=p->count;
        }
    }while(p);
}

int main()
{
    //freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            cin>>dict[i];
        CreateTrie();
        cin>>query;

        const char* cstr=query.c_str();
        for(int starti=0;starti<query.size();starti++)
        {
            find(cstr+starti);
        }
        printf("%d\n",ans);
    }

    return 0;
}

然后几天看了下瞿神的matrix store的 trie,万物都是图。同时memset(ch[0],-1, sizeof(ch[0]));

这种写法其实是-1 变成32bit1,然后初始化ch[0]指向的30个int,sizeof(ch[0])其实是不如sizeof(int)*30, 因为传入函数可能数组名退化为指针,当然一般使用全局的都没有太大的差别。

Posted by richard爱闹 - 9月 2 2014
如需转载,请注明: 本文来自 Richard