【ZOJ3228】白濑肆の完美算法教室补番作,0.7%达成 白濑肆×AC自动机

5 篇文章 0 订阅
3 篇文章 0 订阅
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <queue>

using namespace std;

/**
    就是一个AC自动机
    但是每个单词有两个状态,[0]是可以overlap,[1]是不能overlap时候的次数
    我们用last数组记录下每个不能overlap的单词出现的位置,如果
    当前位置 - last[word] + 1 > strlen(word),那么证明该单词并未overlap,更新last数组,对应次数+1即可
    补番作,水题。
*/
const int MAX_ALPHABET = 27;
const int MAX_TEXT = 100010;
const int MAX_WORD = 7;
const int MAX_WORD_NUM = 100010;
struct node
{
    int id;
    int cnt;
    int len;
    node* fail;
    node* next[MAX_ALPHABET];
    node()
    {
        id = -1; //指定该单词出现的位置。
        cnt = 0;
        fail = NULL;
        len = 0;
        memset(next,0,sizeof(next));
    }
};
int cnt;
int loc[MAX_WORD_NUM];
int type[MAX_WORD_NUM];
int strcnt[MAX_WORD_NUM][2]; // 每个词儿两个状态
int last[MAX_WORD_NUM]; //每个词最后一次出现的地方。
queue<node*> q;
变量区结束///
int AC_insert(node* root,char* dest,int flag)
{
    node* tmp = root;
    int l = strlen(dest);
    for(int i = 0 ; i < l ; i++)
    {
        int k = dest[i]-'a';
        if( tmp->next[k] == NULL )
        {
            tmp->next[k] = new node();
        }
        tmp = tmp->next[k];
    }
    if( tmp->id == -1)
    {
        tmp->id = cnt;
        cnt++;
    }
    tmp->cnt = tmp->cnt | flag;
    tmp->len = l;
    return tmp->id;  //标明该单词在单词列表中第几个出现。。。
}
void AC_build(node* root)
{
    root->fail = NULL;
    q.push(root);
    while(!q.empty())
    {
        node* tmp = q.front();
        q.pop();
        node *p;
        for(int i = 0 ; i < 26 ; i++)
        {
            if(tmp->next[i])
            {
                if( tmp == root )
                {
                    tmp->next[i]->fail = root;
                }
                else
                {
                    p = tmp->fail;
                    while(p)
                    {
                        if( p->next[i] )
                        {
                            tmp->next[i]->fail = p->next[i];
                            break;
                        }
                        p = p->fail;
                    }
                    if( !p )
                        tmp->next[i]->fail = root;
                }
                q.push(tmp->next[i]);
            }
        }
    }
}
void AC_search(node* root,char* dest)
{
    node* tmp;
    node* p;
    p = root;
    for(int i = 0 ; dest[i] ; i++)
    {
        int ind = dest[i] - 'a';
        while( p->next[ind]==NULL && p != root)
            p = p->fail;
        p = p->next[ind];
        if( p == NULL )
            p = root;
        tmp = p;
        while( tmp != NULL )
        {
            if( tmp->id != -1 )
            {
                if( tmp->cnt & 1 )
                {
                    strcnt[tmp->id][0]++;
                }
                if( tmp->cnt & 2 )
                {
                    if( i - tmp->len + 1 > last[tmp->id] )
                    {
                        strcnt[tmp->id][1]++;
                        last[tmp->id] = i;
                    }
                }
            }
            tmp = tmp->fail;
        }
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("B:\\acm\\SummerVacation\\String-I\\B.in","r",stdin);
    freopen("B:\\acm\\SummerVacation\\String-I\\B.out","w",stdout);
#endif
    char dest[MAX_TEXT];
    char word[MAX_WORD];
    int t = 1;
    while(scanf("%s",dest) != EOF)
    {
        memset(last,-1,sizeof(last));
        memset(strcnt,0,sizeof(strcnt));
        cnt = 1;
        node* root = new node();


        int N;
        scanf("%d",&N);
        for(int i = 0 ; i < N ; i++)
        {
            scanf("%d %s",&type[i],word);
            loc[i] = AC_insert(root,word,type[i]+1);
        }
        AC_build(root);
        AC_search(root,dest);
        printf("Case %d\n",t++);
        int ans;
        for(int i = 0 ; i < N ; i++)
        {
            ans = strcnt[loc[i]][type[i]];
            printf("%d\n",ans);
        }
        printf("\n");
    }
#ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
#endif
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值