kmp

为了学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.

poj 3461

纯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.

poj 3167

牛题

给定长度为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]

你可能感兴趣的:(kmp)