KMP

KMP

求助编辑百科名片
使用KMP加moonlight3.0解码   
使用KMP加moonlight3.0解码

KMP算法是通过分析子串,预先计算每个位置发生不匹配的时候,所需GOTO的下一个比较位置,整理出来一个next数组,然后在上面的算法中使用。

  Knuth-Morris-Pratt Algorithm,简称KMP算法。
  K-multimedia player,简称KMP播放器。

编辑本段朴素算法

  先看看最“朴素”的算法:
  //find a template in a string.
  #include<string.h>
  #include<stdio.h>
  int Index(char *S, char *T, int pos)//pos记录从哪一位开始匹配可以直接用0代替
  {
  int k=pos, j=0;
  while(k <strlen(S) && j<strlen(T))//未超出字符串的长度
  {
  if (S[k] == T[j]) { ++k; ++j;} //如果相同,则继续向后比较
  else {k = k-j+1; j =0;} //如果不同,就回溯,重新查找
  }
  if (j == strlen(T)) return k-strlen(T);
  else return 0;
  }

编辑本段KMP算法

  一种由Knuth(D.E.Knuth)、Morris(J.H.Morris)和Pratt(V.R.Pratt)三人设计的线性时间字符串匹配算法。这个算法不用计算变迁函数δ,匹配时间为Θ(n),只用到辅助函数π[1,m],它是在Θ(m)时间内,根据模式预先计算出来的。数组π使得我们可以按需要,“现场”有效的计算(在平摊意义上来说)变迁函数δ。粗略地说,对任意状态q=0,1,…,m和任意字符a∈Σ,π[q]的值包含了与a无关但在计算δ(q,a)时需要的信息。由于数组π只有m个元素,而δ有Θ(m∣Σ∣)个值,所以通过预先计算π而不是δ,使得时间减少了一个Σ因子。[1]
  KMP算法是通过分析子串,预先计算每个位置发生不匹配的时候,所需GOTO的下一个比较位置,整理出来一个next数组,然后在上面的算法中使用。

编辑本段KMP算法的讲解

  当我们分析一个子串时,例如:abcabcddes. 需要分析一下,每个字符x前面最多有多少个连续的字符和字符串从初始位置开始的字符匹配。然后+1就行了(别忘了,我们的字符串都是从索引1开始的)当然,不要相同位置自己匹配,默认第一个字符的匹配数是0。

编辑本段定义

  设字符串为 x1x2x3...xn ,其中x1,x2,x3,... xi,... xn均是字符,设ai为字符xi对应的整数。则a=m,当且仅当满足如下条件:字符串x1x2...xm equals 字符串x(i-m+1)...xi-1 xi 并且x1x2...xm x(m+1) unequals x(i-m) x(i-m+1)...xi-1 xi。

编辑本段举例

  abcabcddes
  0111234111
  |----------------------默认是0
  --| | |-----------------不能自己在相同位置进行字符匹配,所以这里认为没有匹配字符串,所以0+1 =1,继续从1开始匹配
  ------| | |-----------前面的字符和开始位置的字符相同,所以是2,3,4
  -----------| | | |-------不匹配只能取1。
  希望能明白的是,如果开始字符是 Ch1的话,那么我们就是要在串中第2个Ch1后面的位置开始自己和自己匹配,计算最大的吻合度。
  程序写出来就是:
  void GetNext(char* T, int *next)
  {
  int k=1,j=0;
  next[1]=0;
  while( k〈 T[0] ){
  if (j ==0 || T[k] == T[j])
  {
  ++k;
  ++j;
  next[k] = j;
  }
  else j= next[j];
  }
  }
  但是这个不是最优的,因为他没有考虑aaaaaaaaaaaaaaaaaaab的情况,这样前面会出现大量的1,这样的算法复杂度已经和最初的朴素算法没有区别了。所以稍微改动一下:
  void GetNextEx(char *T, int *next)
  {
  int k=1,j=0; next[1] = 0;
  while(k < T[0])
  {
  if (j == 0 || T[k] == T[j])
  {
  ++k; ++j;
  if (T[k] == T[j])
  next[k] = next[j];
  else
  next[k] = j;
  }
  else j = next[j];
  }
  }
  现在我们已经可以得到这个next字符串的值了,接下来就是KMP算法的本体了:
  相当简单:
  int KMP(char* S, char* T, int pos)
  {
  int k=pos, j=1;
  while (k){
  if (S[k] == T[j]){ ++k; ++j; }
  else j = next[j];
  }
  if (j>T[0]) return k-T[0];
  else return 0;
  }
  和朴素算法相比,只是修改一句话而已,但是算法复杂度从O(m*n) 变成了:O(m)

编辑本段KMP算法的伪代码

  KMP-MATCHER(TP)
  1 nlength[T]
  2 mlength[P]
  3 π ← COMPUTE-PREFIX-FUNCTION(P)
  4 q ← 0 △Number of characters matched.
  5 for i ← 1 to n △Scan the text from left to right.
  6 do while q>0 and P[q+1]≠T[i]
  7 do q ← π[q] △Next character does not match.
  8 if P[q+1]=T[i]
  9 then qq+1 △Next character matches.
  10 if q=m △Is all of P matched?
  11 then print “Pattern occurs with shift” i-m
  12 q ← π[q] △Look for the next match.
  COMPUTE-PERFIX-FUNCTION(P)
  1 mlength[P]
  2 π[1] ← 0
  3 k ← 0
  4 for q ← 2 to m
  5 do while k>0 and P[k+1]≠P[q]
  6 do k ← π[k]
  7 if P[k+1]=P[q]
  8 then kk+1
  9 π[q] ← k
  10 return π[1]

编辑本段KMP算法的c++实现

  //c++实现的KMP算法,所有涉及字符串,其初始下标从0开始(上述算法均是从1开始)
  //example: char s[100],t[100];cin>>s>>t;KMP(s,t);
  //获取待查询模式的next数组
  int* get_next(char* T, int* next){
  int i = 0, j = -1;
  int length = strlen(T);
  int *temp = next;
  *next = -1;
  while(i< length){
  if(j==-1 || *(T+i)==*(T+j)){
  i++;
  j++;
  //优化后的get_next方法,可以防止出现形如"aaaaab"这种模式的计算退化
  if(*(T+i)!=*(T+j))
  *(next+i)=j;
  else
  *(next+i)=*(next+j);
  }
  else
  j=*(next+j);
  }
  return temp;
  }
  //KMP算法
  int KMP(char *S, char *T){
  int S_Length = strlen(S);
  int T_Length = strlen(T);
  //若模式长度大于字符串,则直接返回查询失败
  if( S_Length < T_Length)
  return 0;
  int i = 0, j = 0;
  int* next = new int[T_Length];
  get_next(T, next);
  while(i < S_Length && j < T_Length){
  if(j == -1 || *(S+i) == *(T+j)){
  i++;
  j++;
  }
  else
  j=*(next+j);
  }
  if(j>=T_Length)
  return i-T_Length;
  return 0;
  }
  在此提供一个更简明的适用于字符串的kmp实现:
  #include<iostream>
  #include<string.h>
  int next[100];
  void getnext(char b[])
  {
  int i=1,j=0; //i是每个位子,j是回退的位子
  next[1]=0;
  while(i<=strlen(b))
  {
  if(j==0||b[i-1]==b[j-1])
  {
  i++;
  j++;
  next[i]=j;
  }
  else j=next[j]; //用上一个的 回退关系
  }
  }
  int kmp(char a[],char b[])
  {
  int i=1,j=1; //i是主串中的位子 ,j匹配串的位子
  while(i<=strlen(a)&&j<=strlen(b))
  {
  if(j==0||a[i-1]==b[j-1])
  {
  i++;
  j++;
  }
  else j=next[j];
  }
  if(j>strlen(b))
  return i-strlen(b);
  else return 0;
  }
  int main()
  {
  char a[40],b[40];
  printf("要匹配的主串:\n");
  scanf("%s",a);
  printf("要匹配的子串:\n");
  scanf("%s",b);
  getnext(b);
  printf("输出next值:\n");
  for(int i=1;i<=strlen(b);i++)
  printf("%d ",next[i]);
  printf("\n");
  printf("%d",kmp(a,b));
  system("pause");
  main();
  return 0;
  }

编辑本段串的最大匹配算法

摘要:

  给定两个串S和T,长分别m和n,本文给出了一个找出二串间最大匹配的算法。该算法可用于比较两个串S和T的相似程度,它与串的模式匹配有别。

关键词:

  模式匹配 串的最大匹配 算法
  Algorithm on Maximal Matching of Strings
  Lin YuCai Xiang YongHong Zhang ChunXia Zhang JianJun
  (Computer Science Department of Yunnan Normal University Kunming 650092)
  ABSTRACT Given Two Strings S of length m and T of length n,the paper presents an algorithm which finds the maximal matching of them. The algorithm can be used to compare the similarility of the two strings S and T, it is different with the strings' pattren matching.
  KEY WORDS Pattern Matching Maximal Matching of Strings Algorithm

编辑本段问题的提出

  字符串的模式匹配主要用于文本处理,例如文本编辑。文本数据的存储(文本压缩)和数据检索系统。所谓字符串的模式匹配[2],就是给定两个字符串S和T,长度分别为m和n,找出T中出现的一个或多个或所有的S,在这方面已经取得了不少进展[3][4][5][6][7][8][9][10][11]。本文从文本处理的另一个角度出发,找出两个串的最大匹配,比较其相似程度[1]。它主要应用于文本比较,特别是在计算机辅助教学中。显然前者要找S的完全匹配,而后者并无此要求。例如,若S=ABCD,T=EFABCDX,那么模式匹配的结果就是找出了T中的一个ABCD,而我们算法的结果就是S能与T的ABCD完全匹配,但是T中还有3个字符是比S多出来的,也就是说在S中有100%的字符与T中的匹配,而在T中有57%的字符与S中的匹配。若S= ABCDFE,T=AFXBECDY。则在模式匹配中S与T无匹配项,但在我们的算法中就能发现T中存在A,B,C,D,但D后不存在E,F。而且S中也存在A,B,C,D,且具有顺序性。这样就能公正地评价S,T的区别。得知其相似程度。
  文章的组织如下:首先介绍基本定义和问题的描述;第三节是算法设计;最后是本文总结。

编辑本段问题的描述

  设∑为任意有限集,其元称为字符,w:∑→N为∑到N的函数,称为∑的权函数(注:本文仅讨论权值恒为1的情况)。∑*为∑上的有限字符串集合,那么对任意S,T∈∑*,设S=a1a2…am,T=b1b2…bn,m>0,n>0。记<m>={1,2, …,m},<n>={1,2, …,n},则称{(i,j)∣i∈<m>,j∈<n>,ai=bj}为S与T的匹配关系集,记作M(S,T),称M为S与T的一个(容许)匹配,若对任意(i,j), ( i',j' )∈,① i< i',当且仅当j< j',② i= i'当且仅当j= j'。S与T的匹配中满足 最大者,称为S与T的最大匹配。若C(i,j)为N上的m×n矩阵,且满足:
  则称矩阵C为串S与T的匹配关系阵。
  于是求串S与T的最大匹配,等价于求C中的一个最大独立点集M,它满足,若ci,j,ci',j'∈M,则i< i' 当且仅当j< j',i=i'当且仅当j=j'。我们称这样的最大独立点集为C的最大C-独立点集。
  例:设∑为所有字母的集合,对任意x∈∑,w(x)≡1,设S与T分别为:S=“BOOKNEWS”,T=“NEWBOOKS”。则我们可以得到S与T两个匹配:
  这里=5;
  这里 =4。
  显然为串S与T的最大匹配。
  S与T的匹配关系阵C可表示如下:
  其中带圈的部分为一最大C-独立点集。

编辑本段算法设计

  我们仅就权值为一的情况进行讨论。
  设S和T为任意给定串,C为的S与T匹配关系阵,那么由2的讨论知,求S与T的最大匹配问题,等价于求C的最大C-独立点集问题。因而,为了解决我们的问题,只要给出求C的最大C-独立点集的算法就可以了。
  显然,为了求出C的最大C-独立点集,我们可以采用这样的方法:搜索C的所有C-独立点集,并找出它的最大者。这种方法是可行的,但并不是非常有效的。这会使问题变得很繁,复杂度很大。因此,我们先对问题进行分析。
  在下面的讨论中,我们把C的任一C-独立点集={ai1,j1,…,ais,js},记作=ai1,j1…ais,js,i1 <…< is。于是可看作阵C中以1为节点的一条路,满足:对路中的任意两节点,均有某一节点位于另一节点的右下方。称这种路为右下行路。
  于是求C-独立点集等价于求阵C的右下行路。这种求右下行路的搜索可以逐行往下进行。
  命题1. 若 =αai,jβ和ψ=α'ai,jσ为C的两个C-独立点集,且α为α'的加细,则存在C-独立点集'=αai,jδ,满足≥。
  命题2. 若 =αai,jβ和ψ=α'ai+k,jσ为C的两个C-独立点集,且≥,则存在C-独立点集'=αai,jδ,满足≥。
  命题3. 若 =αai,jβ和ψ=α'ai,j+kσ为C的两个C-独立点集,且≥,则存在C-独立点集'=αai,jδ,满足≥。
  由命题1知,在搜索右下行路的过程中,如果已获得了某一C-独立点集的某一初始截段αai,j和另一C-独立点集ψ的某一初始截段α'ai,j,且有≤,则我们可以停止对ψ的进一步搜索。
  由命题2知,在搜索右下行路的过程中,在某一列j存在某两个C-独立点集的某初始截段=ai1,j1…ais,j和ψ=al1,m1…alt,j,如果≥,但lt>is,则我们可以停止对ψ的进一步搜索。
  由命题3知,在搜索右下行路的过程中,在某一行i存在某两个C-独立点集的某初始截段=ai1,j1…ai,js和ψ=ai1,m1…ai,mt,如果≥,但mt>js,则我们可以停止对ψ的进一步搜索。
  由此可见,并不要求搜索所有C的最大C-独立点集,而可以采用比这简单得多的方法进行计算。那么按照我们上面的三个命题,来看如下实例:
  首先我们得到=B(在上的节点用①表示),我们向右下方找路,可以发现,在第4列有两个1,根据命题2,我们选择上面的一个1,也就是说选择第1行的那个1,而不要第2行的那个1。同时我们也发现在第1行也有两个1,由命题3知,我们选择左边的那个1,即第4列的那个1。此时=BO。但是当我们的算法运行到第4行时,=BOOK,由于K在第3行第6列,而本行的1在第1列,在路最后一个节点K的左边,那么我们必须新建一条路ψ,因为我们并不能确定是否以后就有≥,当算法运行到第6行时,=BOOK,ψ=NEW,=4,=3,我们将S链到路上,此时我们得到最长右下行路=BOOKS,=5。这样我们就可以计算出这两个字符串的匹配程度。
  在我们的算法设计过程中,用到了两个技巧。技巧之一,矩阵C不用存储,是动态建立的,节省了空间。技巧之二,本算法并不要求所有的S与T中所有的元素都相互进行比较,也并不存储所有的右下行路,节省了时间和空间。由矩阵中1的出现情况可见,本算法所需的空间和时间都远小于O(mn)

编辑本段结束语

  本文给出了一个与模式匹配不同的,具有若干应用的,串的最大匹配算法,该算法已经在机器上实现,达到了预期的效果。本文仅讨论权值恒为1的情况,对于权值任意的情形不难由此得到推广。

编辑本段C语言代码(C Code)

  #include<stdio.h>
  #include<string.h>
  void getnext(int next[],char s[],int l)
  {
  int i=1,j=0;
  next[1]=0;
  while(i<l)
  {
  if(j==0 || s[i]==s[j])
  {
  i++;j++;
  next[i]=j;
  }
  else
  j=next[j];
  }
  }
  int KMP(char s1[],char s2[],int l1,int l2,int next[])
  {
  int i,j;
  i=j=1;
  while(i<=l1 && j<=l2)
  {
  if(j==0||s1[i]==s2[j])
  {
  i++;j++;
  }
  else
  j=next[j];
  }
  if(j>l2)
  return(i-l2);
  return 0;
  }
  int main()
  {
  int next[10001],ans,l1,l2;
  char s1[10001],s2[10001];
  scanf("%s",s1+1);
  scanf("%s",s2+1);
  l1=strlen(s1+1);
  l2=strlen(s2+1);
  getnext(next,s2,l2);
  ans=KMP(s1,s2,l1,l2,next);
  if(ans!=0)
  printf("%d\n",ans);
  else
  printf("No!\n");
  system("pause");
  return 0;
  }

编辑本段KMP算法的pascal实现

  var next:array [1 ..1000001] of longint;
  s,t:ansistring;
  procedure get_next(t:ansistring);
  var j,k:integer;
  begin
  j:=1; k:=0;
  while j<length(t) do
  begin
  if (k=0) or (t[j]=t[k]) then
  begin
  inc(j);
  inc(k);
  next[j]:=k;
  end
  else k:=next[k];
  end;
  end;
  function index(s:ansistring;t:ansistring):longint;
  var i,j:longint;
  begin
  get_next(t);
  index:=0;
  i:=1; j:=1;
  while (i<=length(s))and(j<=length(t)) do
  begin
  if (j=0)or(s[i]=t[j]) then
  begin
  inc(i);
  inc(j);
  end
  else j:=next[j];
  if j>length(t) then index:=i-length(t);
  end;
  end;
  begin
  readln(s);
  readln(t);
  writeln(index(s,t))
  end.

编辑本段KMP播放器

  K-multimedia player的缩写 来自韩国的影音 全能播放器 ,与Mplayer一样从linux平台移植而来的Kmplayer(简称KMP)几乎可以播放您系统上所有的影音文件。通过各种插件扩展KMP可以支持层出不穷的新格式。强大的插件功能,直接从Winamp继承的插件功能,能够直接使用winamp的音频 ,输入,视觉效果插件,而通过独有的扩展能力,只要你喜欢,可以选择使用不同解码器对各种格式进行解码。
  KMPlayer The Professional Media Player!
  它支持 Winamp 2/5 的输入、常规、DSP、视觉效果、媒体库插件。无须注册表支持直接调用 Directshow 滤镜!FFdshow 的视觉特效系统~超强的 GUI 界面~安装电视卡后可以直接代替原软件直接收看电视~支持播放 DVD/VCD 以及绝大多数电脑的媒体文件(AVI 支持 Xvid/DivX/3vid/H264 OGG/OGM/MKV 容器/AC3/DTS 解码~Monkey Audio 解码~)强烈推荐!此播放器除了会将自己的配置信息写入注册表外绝对绿色~
  KMplayer内置目前常见的所有解码器,包括real,QT等。
  另外KMplayer安装版也是目前很少见的检查流氓软件的安装方式,如果一旦有恶意的汉化小组汉化并捆绑了流氓软件。该安装程序自动会识别,并作出提示,建议用户不要安装,虽然不是特别准确,但KMplayer的无广告及第三方插件的特点使其深受好评。
  目前韩国官方已经在Kmplayer里自带了中文字库,只要用户是中文系统,软件就会自动识别,十分方便。
  KMP版本:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值