为了学ac自动机,复习了一下kmp
虽说是复习,但以前学的基本忘光了。。。
推荐matrix67的博文http://www.matrix67.com/blog/archives/115/
做了几道poj
poj 2752
练习求p数组
求串中前缀等于后缀的个数及位置
var n:ansistring; st,p:array[1..400000]of longint; procedure init; var i,j,l,s:longint; begin readln(n); j:=0;l:=length(n); fillchar(p,sizeof(p),0); for i:=2 to l do begin while (j>0)and(n[j+1]<>n[i]) do j:=p[j]; if n[j+1]=n[i] then j:=j+1; p[i]:=j end; fillchar(st,sizeof(st),0);s:=0;i:=l; while i<>0 do begin inc(s);st[s]:=i;i:=p[i] end; for i:=s downto 1 do write(st[i],' '); writeln end; begin assign(input,'2752.in');reset(input); while not seekeof do init; close(input) end.
纯kmp,求模式串在母串中出现次数
var p:array[1..10000]of longint; n,m:ansistring; ans,t:longint; procedure init; var i,j,ln,lm:longint; begin readln(n);readln(m); ln:=length(n);lm:=length(m); fillchar(p,sizeof(p),0); j:=0; for i:=2 to ln do begin while (j>0)and(n[j+1]<>n[i]) do j:=p[j]; if n[j+1]=n[i] then inc(j); p[i]:=j end; j:=0;ans:=0; for i:=1 to lm do begin while (j>0)and(n[j+1]<>m[i]) do j:=p[j]; if n[j+1]=m[i] then inc(j); if j=ln then begin inc(ans); j:=p[j] end end; writeln(ans) end; begin assign(input,'3461.in');reset(input); readln(t); for t:=1 to t do init; close(input) end.
牛题
给定长度为n,与k的串,编号最大值为s
求以串中排名为关键字的模式匹配。
如
5 6 2 10 10 7 3 2 9
1 4 4 3 2 1
该位置就是一个匹配。
1在{1,4,4,3,2,1}中排名为1 2在{2,10,10,7,3,2}中排名也为1
因此1和2匹配,以此类推
这道题解法很多,
有哈希,kmp
kmp中又分不同的匹配标准
我这里采用的标准是依据结论:
两个位置相匹配,当且仅当在各自当前串中这两个位置以前的比他们小的数的个数与和它们相等的数的个数均相等。
也有人依据这个结论改写出三个不等式作为标准,
由于我是直接用结论,所以不赘述
第一个比其小的数相等好理解,但为什么有第二个标准
看下面的例子
2 1
1 1
这两个串本不能匹配,
但只有第一标准,2之前无数,1之前无数,匹配
1之前没有比他小的数,1之前也没有比他小的数,匹配
故此,有了第二标准,因为我们不知整个串的状态,但知道之前匹配的状态
所以要用有限的信息维护我们需要的信息。
信息如何维护,用数据结构,有人用树状数组,我用的是线段树
根据当前串长度进出元素
均摊复杂度o(n*log s)
var m1,s,n,k:longint; st,a:array[1..210000]of longint; b,p,d,d1:array[1..36000]of longint; c:array[1..2,1..64]of longint; procedure origin; begin m1:=1; while m1<s+2 do m1:=m1<<1;m1:=m1-1 end; procedure change(i,x,w:longint); begin x:=x+m1; while x<>0 do begin c[i,x]:=c[i,x]+w; x:=x>>1 end end; function ask(i,l,r:longint):longint; begin if l>r then exit(0); ask:=0; l:=l+m1-1;r:=r+m1+1; while not(l xor r=1) do begin if l and 1=0 then ask:=ask+c[i,l+1]; if r and 1=1 then ask:=ask+c[i,r-1]; l:=l>>1;r:=r>>1 end end; procedure init; var i,j,l:longint; begin readln(n,k,s); fillchar(a,sizeof(a),0);fillchar(b,sizeof(b),0); fillchar(p,sizeof(p),0);fillchar(c,sizeof(c),0); fillchar(d,sizeof(d),0);fillchar(d1,sizeof(d1),0); fillchar(st,sizeof(st),0); for i:=1 to n do readln(a[i]); for i:=1 to k do readln(b[i]); origin; for i:=1 to k do begin d[i]:=ask(1,1,b[i]-1);d1[i]:=c[1,b[i]+m1]; change(1,b[i],1); end; j:=0; fillchar(c[1],sizeof(c[1]),0); for i:=2 to k do begin while (j>0)and((d[j+1]<>ask(1,1,b[i]-1))or(d1[j+1]<>c[1,b[i]+m1])) do begin l:=i-j; j:=p[j]; if j<>0 then for l:=l to i-j-1 do change(1,b[l],-1) else for l:=l to i-1 do change(1,b[l],-1) end; if (d[j+1]=ask(1,1,b[i]-1))and(d1[j+1]=c[1,b[i]+m1]) then inc(j); if j=0 then j:=1; if j=1 then fillchar(c[1],sizeof(c[1]),0); change(1,b[i],1); p[i]:=j end; j:=0;s:=0; fillchar(st,sizeof(st),0); for i:=1 to n do begin if i=55927 then k:=k; while (j>1)and((d[j+1]<>ask(2,1,a[i]-1))or(d1[j+1]<>c[2,a[i]+m1])) do begin l:=i-j; j:=p[j]; if j<>0 then for l:=l to i-j-1 do change(2,a[l],-1) else for l:=l to i-1 do change(2,a[l],-1) end; if (d[j+1]=ask(2,1,a[i]-1))and(d1[j+1]=c[2,a[i]+m1]) then inc(j); if j=0 then j:=1; if j=1 then fillchar(c[2],sizeof(c[2]),0); change(2,a[i],1); if j=k then begin inc(s);st[s]:=i-j+1; l:=i-j+1; j:=p[j]; if j<>0 then for l:=l to i-j do change(2,a[l],-1) else fillchar(c[2],sizeof(c[2]),0) end end; writeln(s); for i:=1 to s do writeln(st[i]) end; begin assign(input,'3167.in');reset(input); assign(output,'3167.out');rewrite(output); while not seekeof do init; close(input);close(output) end.
为了对拍这道题,
特地到jansonzhu8的博客里淘到了(基哥发明)
在各种环境下均可用的一个很棒的对拍程序 for pascal
下面是改编版
program comp; uses crt,dos; var a,b:string; procedure rd(f:string;var a:string); var b:string; begin assign(input,f);reset(input); fillchar(a,sizeof(a),0); while not seekeof do begin readln(b); a:=a+b end; close(input); end; begin repeat exec('data.exe',''); exec('3167.exe',''); exec('std.exe',''); rd('3167.out',a); rd('3167.ans',b); writeln(a=b); until {readkey='x'}a<>b; end.
注意在Ubuntu中无exe后缀
最后看ppt发现的结论:最短重复字串长度 n-p[n]