关闭

【NOIP2013模拟】归途与征程 题解+代码

259人阅读 评论(0) 收藏 举报
分类:

Description

这里写图片描述

Input

第一行为字符串A。
第二行为字符串B。

Output

输出在B的所有循环同构串中,有多少个能够与A匹配。

Sample Input

输入1:
aaaa
aaaa
输入2:
a*a
aaaaaa
输入3:
* a*b*c *
abacabadabacaba

Sample Output

输出1:
4
输出2:
6
输出3:
15

Data Constraint

对于30%的数据,M<=20;
对于80%的测试点,M<=200;
对于100%的测试点,1<=N<=100,1<=M<=100000。

Solution

30%随便爆搜
80%发现O(n3)甚至更多一点都能过,那么就准备好有一点水准的暴力
分析题目,可以发现,实质上就是字符串A被分成若干个小字符串,每个小字符串要按顺序分别与字符串B匹配即可。不过,如果前后不是的话就把前后先走到然后再做。之后说的开头或者结尾都是以开头或结尾的。
先把B复制一份,然后可以用两个指针i和j,分别指着A的开头和枚举的B的开头,一个个做,以为分界点。如果当前字符匹配成功,就把i+1,j+1。如果当前字符匹配失败,就把i还原到这一个小字符串的开头,j变成这次匹配之前的位置+1,知道i超过A的长度或j超过应到达的地方就结束。
最后,只有i和j都超过了结尾,才能算一个匹配成功。O(n2)或更大一些。
100%
有一种DP的做法,但是我用的是80分的爆搜加个优化。
B已经被复制。
A串被分割之后,从1开始给每个小字符串标号。用一个预处理F[i,j]表示字符串B从i这个字符之后,第j个小字符串最早从B串的哪个位置开始。那么每次就只用跳到对应的位置就行了,如果已经跳过了n个位置,却还没有把所有小字符串做完,就不可行。单次匹配时间复杂度降为O(m)
怎么预处理F呢?思考一下要做的是什么。在一个字符串里面匹配字符串。很显然,用KMP就行了。KMP模板
另:调试时不要相信PASCAL

Code

var
    n,i,j,k,kk,ans,m,l,q,jy,jy2:longint;
    t,s,s1:ansistring;
    ch:char;
    bz,bz2:boolean;
    next,long:array[1..110] of longint;
    a:array[1..200020,1..110] of longint;
procedure getnext(s:ansistring);
var
    i,j,n:longint;
begin
    n:=length(s);j:=0;
    for i:=2 to n do
    begin
        while (j>0)and(s[i]<>s[j+1]) do j:=next[j];
        if (s[i]=s[j+1]) then inc(j);
        next[i]:=j;
    end;
end;
procedure kmp(s:ansistring;k:longint);
var
    m,i,j:longint;
begin
    j:=0;m:=length(s);
    for i:=2 to 2*n do
    begin
        while (j>0)and(t[i]<>s[j+1]) do j:=next[j];
        if (t[i]=s[j+1]) then inc(j);
        if j=m then begin a[i-long[k]+1,k]:=i-long[k]+1;j:=next[j];end;
    end;
end;
begin
    readln(s);readln(t);
    m:=length(s);n:=length(t);t:=t+t;s:=s+'!';t:=t+'@';i:=1;kk:=0;
    while i<=m do
    begin
        s1:='';while (s[i]='*')and(i<=m) do inc(i);
        while (s[i]<>'*')and(i<=m) do begin s1:=s1+s[i];inc(i);end;
        if (i<=m) then
        begin
            fillchar(next,sizeof(next),0);getnext(s1);
            inc(kk);long[kk]:=length(s1);kmp(s1,kk);
        end;
    end;
    for i:=1 to kk do
    begin
        for j:=2*n downto 1 do
        begin
            if (j=2*n)and(a[j,i]=0) then a[j,i]:=2*n+1;
            if a[j,i]=0 then a[j,i]:=a[j+1,i];
        end;
    end;
    for i:=1 to n do
    begin
        bz:=true;
        j:=1;k:=i;q:=kk;l:=i+n-1;
        if (s[m]<>'*') then
        begin
            while (s[m+l-i-n+1]<>'*')and(l>=k) do
            begin
                if s[m+l-i-n+1]=t[l] then dec(l)
                else begin bz:=false;break;end;
            end;
            dec(q);
        end;
        if (s[1]<>'*') then
        begin
            while (s[k-i+1]<>'*')and(k<=l) do
            begin
                if s[k-i+1]=t[k] then inc(k)
                else begin bz:=false;break;end;
            end;
            inc(j);
        end;
        if bz=false then continue;
        while (j<=q)and(k<=l) do
        begin
            k:=a[k,j]+long[j];inc(j);
        end;
        if (bz)and(j>q)and(k<=l+1) then inc(ans);
    end;
    writeln(ans);
end.
1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:73436次
    • 积分:3453
    • 等级:
    • 排名:第10086名
    • 原创:260篇
    • 转载:2篇
    • 译文:0篇
    • 评论:32条
    博客专栏
    最新评论