HDU3336

看到题目的第一反应是:这不是AC自动机模板么…每次截取子串然后添加到trie树中,然后跑一遍匹配即可…然后妥妥tle了…正解应该基于这样的考虑:在kmp算法中,我们的next数组其实是对应了模式串在当前位置能否在之前的位置中找到与当前后缀相同的前缀字符串,那么如果当前位置对应的next不是0的话,就说明前缀字符串在当前位置找到了一个匹配,也就是题目中的要求;因此,其实统计一遍next即可;
但是要注意两点:1.next对应值为0或-1时不匹配.因为位置为0其实是说明当前位置不存在相同的后缀。
2.比较next与平时不一样,平时只要比较到size()-1,因为最后一个如果相同,那么就可以滚了。但是现在是要统计出现的次数,所以要比较到最后一位的后一位。
暴力:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int mod=10007;
struct node{
    node*next1[26];
    node*fail;
    int sum;
    node(){
        memset(next1,0,sizeof(next1));fail=NULL;sum=0;
    }
}*root=new node();
int cnt=0;
void insert(string str)
{
    node*p=root;
    for(int i=0;i<str.size();i++){
        int x=str[i]-'a';
        if(!p->next1[x]){
            node*t=new node();p->next1[x]=t;
        }
        p=p->next1[x];
    }
    p->sum++;
}
void build_fail_pointer(void)
{
    node*p,*temp;
    queue<node*>que;que.push(root);
    while(!que.empty()){
        temp=que.front();que.pop();
        for(int i=0;i<26;i++){
            if(temp->next1[i]){
                if(temp==root){
                    temp->next1[i]->fail=root;
                }
                else{
                    p=temp->fail;
                    while(p){
                        if(p->next1[i]){
                            temp->next1[i]->fail=p->next1[i];break;
                        }
                        p=p->fail;
                    }
                    if(!p)temp->next1[i]->fail=root;
                }
                que.push(temp->next1[i]);
            }
        }
    }
}
void ac_automation(string str)
{
    node*p=root;
    for(int i=0;i<str.size();i++){
        int x=str[i]-'a';
        while(!p->next1[x]&&p!=root)p=p->fail;
        p=p->next1[x];
        if(!p)p=root;
        node*temp=p;
        while(temp!=root){
            if(temp->sum){
                cnt++;if(cnt==mod)cnt=0;
            }
            //else break;//**如果不只是需要知道模式串是否出现过而**
            //**是要找出所有出现的位置的话,这一行是不能要的!!!!!!**
            //并且,根据观察,没有这一行似乎并不会导致答案错误,只是时间会增加;
            //但是,有这一行却可能导致答案错误
            temp=temp->fail;
        }
    }
}
void dl(node*p)
{
    for(int i=0;i<26;i++)
        if(p->next1[i])dl(p->next1[i]);
    if(p!=root)delete p;
}
int main()
{
    freopen("test.in"  ,"r",stdin);
    freopen("test2.out","w",stdout);
    int n,i,j;string str;int t;
    cin>>t;
    while(t--){
        cin>>n;getchar();getline(cin,str);string temp;
        dl(root);for(i=0;i<26;i++)root->next1[i]=NULL;root->fail=NULL;root->sum=0;
        for(i=1;i<=n;i++){
            temp=str.substr(0,i);insert(temp);
        }
        build_fail_pointer();
        cnt=0;ac_automation(str);
        cout<<cnt<<endl;
    }

    return 0;
}

正解:

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int next1[200005];
const int mod=10007;
void getnext(string str)
{
    next1[0]=-1;
    int j=0,k=-1;
    while(j<str.size()){
        if(k==-1||str[j]==str[k]){
            j++;k++;next1[j]=k;
        }
        else{
            k=next1[k];
        }
    }
}
int main()
{
    freopen("test.in"  ,"r",stdin);
    freopen("test1.out","w",stdout);
    int t,n,i,j;string str;
    cin>>t;
    while(t--){
        cin>>n;getchar();getline(cin,str);
        getnext(str);i=0,j=0;int ans=str.size()%mod;
        while(i<=str.size()){
            j=i;i++;//cout<<next1[j]<<endl;
            while(next1[j]>0){
                ans+=1;j=next1[j];
            }
            ans%=mod;
        }
        cout<<ans<<endl;
    }

    return 0;
}

数据生成器:

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<ctime>
using namespace std;
int main()
{
    freopen("test.in","w",stdout);
    srand(time(0));
    int t=rand()%100+1;
    while(t--){
        int len=rand()%100+1;
        cout<<len<<endl;
        for(int i=1;i<=len;i++){
            cout<<char(rand()%26+'a');
        }
        cout<<endl;
    }

    return 0;
}

比较:

#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
int main()
{
    int tmp=0;
    for(int i=1;i<=100;i++){
        system("./data");
        system("./test1");
        system("./test2");

        //if(i/100>tmp) {printf("-- %d --\n",i);tmp=i/100;}
        if(system("diff test1.out test2.out"))
        {
            printf("wrong in --> %d \n",i);
            break;
        }
        else cout<<"Test #"<<i<<" :No problem!"<<endl;
    }
    return 0;
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值