一些算法拾贝

长平狐 发布于 2013/01/06 11:22
阅读 55
收藏 0

kmp算法(算法是转的)+代码

 

此算法的确很难理解。但是只要你花耐性去理解;应该是可以理解的;!

所以叫做KMP,是因为这个算法是由Knuth、Morris、Pratt三个提出来的,取了这三个人的名字的头一个字母。这时,或许你突然明白了AVL 树为什么叫AVL,或者Bellman-Ford为什么中间是一杠不是一个点。有时一个东西有七八个人研究过,那怎么命名呢?通常这个东西干脆就不用人名字命名了,免得发生争议,比如“3x+1问题”。扯远了。
     个人认为KMP是最没有必要讲的东西,因为这个东西网上能找到很多资料。但网上的讲法基本上都涉及到“移动(shift)”、“Next函数”等概念,这非常容易产生误解(至少一年半前我看这些资料学习KMP时就没搞清楚)。在这里,我换一种方法来解释KMP算法。

     假如,A="abababaababacb",B="ababacb",我们来看看KMP是怎么工作的。我们用两个指针i和j分别表示,A[i-j+ 1..i]与B[1..j]完全相等。也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就说B是A的子串(B串已经整完了),并且可以根据这时的i值算出匹配的位置。当A[i+1]<>B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。我们看一看当 i=j=5时的情况。

     i = 1 2 3 4 5 6 7 8 9 ……
     A = a b a b a b a a b a b …
     B = a b a b a c b
     j =   1 2 3 4 5 6 7

     此时,A[6]<>B[6]。这表明,此时j不能等于5了,我们要把j改成比它小的值j'。j'可能是多少呢?仔细想一下,我们发现,j'必须要使得B[1..j]中的头j'个字母和末j'个字母完全相等(这样j变成了j'后才能继续保持i和j的性质)。这个j'当然要越大越好。在这里,B [1..5]="ababa",头3个字母和末3个字母都是"aba"。而当新的j为3时,A[6]恰好和B[4]相等。于是,i变成了6,而j则变成了 4:

     i = 1 2 3 4 5 6 7 8 9 ……
     A = a b a b a b a a b a b …
     B =      a b a b a c b
     j =         1 2 3 4 5 6 7

     从上面的这个例子,我们可以看到,新的j可以取多少与i无关,只与B串有关。我们完全可以预处理出这样一个数组P[j],表示当匹配到B数组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。
     再后来,A[7]=B[5],i和j又各增加1。这时,又出现了A[i+1]<>B[j+1]的情况:

     i = 1 2 3 4 5 6 7 8 9 ……
     A = a b a b a b a a b a b …
     B =      a b a b a c b
     j =     1 2 3 4 5 6 7

     由于P[5]=3,因此新的j=3:

     i = 1 2 3 4 5 6 7 8 9 ……
     A = a b a b a b a a b a b …
     B =         a b a b a c b
     j =         1 2 3 4 5 6 7

     这时,新的j=3仍然不能满足A[i+1]=B[j+1],此时我们再次减小j值,将j再次更新为P[3]:

     i = 1 2 3 4 5 6 7 8 9 ……
     A = a b a b a b a a b a b …
     B =             a b a b a c b
     j =            1 2 3 4 5 6 7

     现在,i还是7,j已经变成1了。而此时A[8]居然仍然不等于B[j+1]。这样,j必须减小到P[1],即0:

     i = 1 2 3 4 5 6 7 8 9 ……
     A = a b a b a b a a b a b …
     B =               a b a b a c b
     j =             0 1 2 3 4 5 6 7

     终于,A[8]=B[1],i变为8,j为1。事实上,有可能j到了0仍然不能满足A[i+1]=B[j+1](比如A[8]="d"时)。因此,准确的说法是,当j=0了时,我们增加i值但忽略j直到出现A[i]=B[1]为止。

 

 

据上,本人的代码:
#include <iostream>
using namespace std;
int next[100];
char A[100],B[100];
int kmp(char *a,char *b)
{
 int i,j;
 i=j=0;
 int la=strlen(a);
 int lb=strlen(b);
 while(i<la)
 {
  if(a[i]==b[j])
  {
   if(j==lb-1)//////////////!
    return i-lb+1;////////!
   i++;
   j++;
  }
  else if(j>0)
  {
   j=next[j-1];///////////!!!不相等时,j就移动到前一个字母的NEXT;
  }
  else
   i++;
 }
 return -1;

}
void get_next(char *p)

 int plen,i,j;
 plen=strlen(p);  
 i=1;j=0;    
 next[j]=0;
 while(i<plen)
 {  
      if(p[i]==p[j])//开始有周期
   {
    next[i]=j+1;///!!!
    i++;
    j++;
   }  
      else if(j>0)//结束该周期
   {
    j=0; ////////next[j-1];
   }
   else/////////周期没有开始都置0;
   {
    next[i]=0;
    i++;
   }
 }
   /*for(i=0;i<7;i++)
  cout<<next[i]<<endl;*/


int main()
{
 scanf("%s",A);/////////测试数据abababaababacb;结果为7;
 scanf("%s",B);/////////测试数据ababacb;结果为0012300;
 get_next(B);
 printf("%d/n",kmp(A,B));
 return 0;
}

补充下:
getnext部分:
i=             0   1   2   3   4   5   6; i为该串的移动下标;


位置:       1   2   3   4   5   6   7
               a   b   a   b   a   c   b

 

j=            0   0   1   2   3   0   0; j为next坐标;同时也是周期的坐标;(举个例子;next[2]=j=1时,表示该a在1的位置也有一个a);

 

next=     0   0   1   2   3   0   0
           [0]  [1] [2] [3] [4] [5] [6]

 

转载声明: 本文转自 http://blog.163.com/wojiaojason@126/blog/static/124098284201022132550944/?fromdm&fromSearch&isFromSearchEngine=yes

=========================================================================

拓扑排序

 

#include <iostream>
using namespace std;
int n,m,cnt[27],sum[27],map[27][27],pr[27];
char str[5];
int toposoft()
{
 int i,j,num,now,s,flag;
 now=flag=0;////////////////!!!
 memset(pr,0,sizeof(pr));
 for(i=1;i<=n;i++)
  cnt[i]=sum[i];///////////用CNT记录,免得修改了sum数组的值;
 for(i=1;i<=n;i++)
 {
  num=0;////////!!!!!!!!!!!!!
  for(j=1;j<=n;j++)
   if(cnt[j]==0)////////查找入度为0的点,记录入度为0的点;
   {
    num++;
    s=j;
   }
  if(num==0)
   return 0;/////////////入度为0的点一个都没有;
  if(num>1)//////////当入度为0的点只有一个是,该序列才有顺序;
   flag=1;
  pr[now++]=s;///入列
  cnt[s]=-1;//////////已经入列的点标记;
  for(j=1;j<=n;j++)
   if(map[s][j]==1)
    cnt[j]--;///////////////删除边;
 }
 if(flag)
  return -1;
 return 1;

}
int main()
{
 while(1)
 {
  scanf("%d%d",&n,&m);
  if(n==0&&m==0)
   break;
  memset(map,0,sizeof(map));
  memset(sum,0,sizeof(sum));
  memset(cnt,0,sizeof(cnt));
  int sign=0,i;
  for(i=1;i<=m;i++)
  {
   cin>>str;///////////////!!!!
   if(sign)
    continue;///////////////!!!
   int u=str[0]-'A'+1;
   int v=str[2]-'A'+1;
   map[u][v]=1;
   sum[v]++;
   int ans=toposoft();
   if(ans==0)
   {
    printf("Inconsistency found after %d relations./n",i);
    sign=1;
   }
   if(ans==1)
   {
    printf("Sorted sequence determined after %d relations: ",i);
    for(int j=0;j<n;j++)
     putchar(pr[j]+'A'-1);
    printf("./n");
    sign=1;
   }
  }
  if(!sign)
   printf("Sorted sequence cannot be determined./n");
  }

 return 0;
}

 

转载声明: 本文转自 http://blog.163.com/wojiaojason@126/blog/static/1240982842010214105332122/

=========================================================================

 


原文链接:http://blog.csdn.net/sunboy_2050/article/details/5673765
加载中
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部