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

37 篇文章 0 订阅
21 篇文章 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值