AC自动机通配符匹配

计算机软件)技术中,通配符可用于代替字符。 通常地,星号“*”匹配0个或以上的字符,问号“?”匹配1个字符。(wiki百科)

今天做Leetcode上的一道题时不会做,网上查到了这么一种做法,当年打比赛的时候都没有碰到过。。。。

Leetcode Wildcard Matching

递归做法TLE

class Solution {
public:
    bool isMatch(const char *s, const char *p) {
        char cs = *s;
        char cp = *p;
        if(cp == ‘\0‘) {
            return cs == cp;
        } else if (cp == ‘?‘) {
            if (cs == ‘\0‘) return false;
            return isMatch(s + 1,p + 1);
        } else if (cp == ‘*‘) {
            const char *st = s;
            while(cp == ‘*‘){
                p++;
                cp = *p;
            }
            for(; *st != ‘\0‘; ++st) {
                if (isMatch(st, p)) return true;
            }
            return isMatch(st,p);
        } else if (cp != cs)
            return false;
        return isMatch(s + 1,p + 1);
    }
};

非递归改改AC

class Solution {
public:
    bool isMatch(const char *s, const char *p) {  
        // Start typing your C/C++ solution below  
        // DO NOT write int main() function  
           
        const char* star=NULL;  
        const char* ss=s;   
        while (*s){  
            if ((*p==‘?‘)||(*p==*s)){s++;p++;continue;}  
            if (*p==‘*‘){star=p++; ss=s;continue;}  //star可以更新,使用贪心法
            if (star){ p = star+1; s=++ss;continue;}
            return false;  
        }  
        while (*p==‘*‘){p++;}  
        return !*p;  
    }  

};
这段代码我参考的http://blog.csdn.net/doc_sgl/article/details/12721187,代码短小精悍,还是知道仔细体会。



HDU 3901
关于AC自动机通配符匹配的相关知识请看点击打开链接

#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define N 100005
#define B 26

int tree[N][B],size;
vector<int> key[N];
int cnt[N],tcnt;
void build(char str[]){
    size=tcnt=0;
    memset(tree[0],0,sizeof(tree[0]));
    key[0].clear();

    int node=0;
    for(int i=0;!i||str[i-1];i++){
        if(str[i]==‘?‘||str[i]==0){
            if(node){
                key[node].push_back(i-1);
                node=0; tcnt++;
            }
            continue;
        }else{
            int c=str[i]-‘a‘;
            if(!tree[node][c]){
                tree[node][c]=++size;
                memset(tree[size],0,sizeof(tree[size]));
                key[size].clear();
            }
            node=tree[node][c];
        }
    }
}

int que[N];
int fail[N];
void getfail(){
    int *s=que,*e=que;
    for(int i=0;i<B;i++){
        if(tree[0][i]){
            fail[tree[0][i]]=0;
            *e++=tree[0][i];
        }
    }
    while(s!=e){
        int p=*s++;
        for(int i=0;i<B;i++){
            if(tree[p][i]){
                int v=tree[p][i];
                *e++=v;
                fail[v]=tree[fail[p]][i];
                if(key[fail[v]].size()){
                    key[v].insert(key[v].end(),
                            key[fail[v]].begin(),key[fail[v]].end());
                }
            }else{
                tree[p][i]=tree[fail[p]][i];
            }
        }
    }
}

int find(char str[]){
    if(tcnt==0) return 0;
    int node=0;
    for(int i=0;str[i];i++){
        node=tree[node][str[i]-‘a‘];
        cnt[i] = 0; 
        for(int j=0;j<key[node].size();j++){
            if(i>=key[node][j]){
                cnt[i-key[node][j]]++;
                if(cnt[i-key[node][j]]==tcnt) return i-key[node][j];
            }
        }
    }
    return -1;
}

char tmp[N];
char src[N];
char wild[N];
bool match(int ls, int lp){
    int tl=0;
    for(int i=0;i<lp;i++) if(wild[i]!=‘*‘) tl++; 
    if(tl>ls) return false;

    int s=-1;
    for(int i=0;i==0||src[i-1];i++){   //如果最左不是*需要从头开始匹配到第一个*
        if((src[i]!=0&&wild[i]==‘?‘)||src[i]==wild[i]) continue;
        if(wild[i]==‘*‘) s=i+1;
        else return false;
        break; 
    }
    if(s==-1) return true; 
    
    for(int i=ls-1,j=lp-1;;i--,j--){   //如果最右不是*需要从结尾开始倒着匹配到倒数第一个*
        if(i==-1&&j==-1) break;
        if(i==-1){
            if(wild[j]!=‘*‘) return false; 
            break;
        }
        if(j==-1) return false; 
        if(src[i]==wild[j]||wild[j]==‘?‘){
            src[ls=i] = wild[j] = 0;
        } else if(wild[j]==‘*‘) break;
        else return false;
    }

    int ts=s-1;
    for(int i=1;i<lp-tl;i++){       //剩下的部分为*......*将其中每个**中间部分用AC自动机匹配即可
        if(wild[s]==‘*‘){s++;continue;}
        int len = 0;
        for(;wild[s]!=‘*‘&&wild[s];s++){
            tmp[len]=wild[s];
            tmp[++len]=0;
        }
        s++;
        build(tmp);
        getfail();    //这两步构造AC自动机
        int pos=find(src+ts);   //寻找最左边匹配位置
        if(pos==-1) return false; 
        else{
            ts+=pos+len;
            if(ts>ls) return false; 
        }
    }
    return true; 
} 

int main(){
    while(~scanf("%s", src)){
        scanf("%s", wild);
        puts(match(strlen(src), strlen(wild))?"YES":"NO"); 
    }
}




参考文章

http://www.cnblogs.com/ambition/archive/2011/08/26/wildcard.html

http://www.cnblogs.com/zcwwzdjn/archive/2012/03/10/2389514.html




AC自动机通配符匹配

上一篇:求一个十进制数中含有的二进制一的个数


下一篇:使用反射机制深入理解AOP机制并自定义AOP管理模块