KMP总结

来源:互联网 发布:飞行器制造就业数据 编辑:程序博客网 时间:2024/04/27 20:02

当一个字符串以0为起始下标时,next[i]可以描述为"不为自身的最大首尾重复子串长度"也就是说,从模式串T[0...i-1]的第一个字符开始截取一段长度为m(m < i-1)子串,再截取模式串T[0...i-1]的最后m个字符作为子串,如果这两个子串相等,则该串就是一个首尾重复子串。我们的目的就是要找出这个最大的m值。例如:


i = 4 ,则 i - 1 = 3 m = next[4] = 2

T[0...3]截取长度为2的子串,为"ab"

T[0..3]截取最后2个字符,为"ab"

此时2个子串相等,则说明 next[4] = 2成立,也可证明 m = 2为最大的m值。

求模式串在匹配串中出现的次数(可重复计算如aa在aaa中出现2次)。

char  word[100008] , text[1000008] ;int   next[100008] ;void  getnext(char s[]){      int len = strlen(word) ;      next[0] = -1 ;      int i = 0 ,  j = -1 ;      while(i < len){           if(j == -1 || s[i] == s[j])  next[++i] = ++j ;           else  j = next[j] ;      }}int   kmp(){      getnext(word) ;      int lword = strlen(word) , ltext = strlen(text) ;      int s = 0 , i =0 , j = 0 ;      while(i < ltext){           while(i < ltext && j < lword){                if(j == -1 || text[i] == word[j]){ i++ ; j++ ;}                else   j = next[j] ;                if(j == lword){                    s++ ;                    j = next[j] ;//若不能重复计算则j=0                }           }      }      return s ;}

Power Strings

abcd  1
aaaa 4
ababab 3
           getnext(word) ;           int  len = strlen(word) ;           int  x = len - next[len] ;           if(len % x == 0) printf("%d\n" , len/x) ;           else             puts("0") ;

求最少在末尾添加几个字符,使得串成为循环串

           getnext(word) ;           len = strlen(word) ;           minrepeat = len - next[len] ;           if(minrepeat == len) printf("%d\n" ,len) ;           else if(len % minrepeat == 0) puts("0") ;           else  printf("%d\n" , minrepeat - len%minrepeat) ;

求前缀的循环周期

示例:abababab
前4个字符,循环字串为ab,有2个循环周期 ab|ab
前6个字符,循环字串为ab,有3个循环周期 ab|ab|ab
前8个字符,循环字串为ab,有4个循环周期 ab|ab|ab|ab
输出:
4 2
6 3
8 4

           getnext(word) ;           for(i = 2 ; i <= len ; i++){                if(i%(i-next[i])==0 && next[i]!=0)                    printf("%d %d\n" ,i , i/(i-next[i])) ;           }

求给定字符串含前缀的数量

abab  前缀为a , ab ,aba,abab  ,abab中共有六个子串是前缀a a ab ab aba abab  答案为6

dp[i]:以string[i]结尾的子串总共含前缀的数量

dp[i=dp[next[i]]+1,即以i结尾的子串中含前缀的数量加上前j个字符这一前缀

           getnext(word) ;           s = dp[0] = 0 ;           for(i = 1 ; i <= n ; i++){                 dp[i] = dp[next[i]] + 1 ;                 s = (s + dp[i]) % 10007 ;           }           printf("%d\n" , s)  ;

求最长的a的前缀同时满足是b的后缀

           int s1 = strlen(word) ;           int s2 = strlen(text) ;           strcat(word , text) ;           getnext(word) ;           int i = s1+s2 ;           while(next[i]>0 && (next[i]>s1 || next[i]>s2 )) i = next[i] ;           if(next[i]){                word[next[i]] = '\0' ;                printf("%s %d\n" , word , next[i]) ;           }           else  puts("0") ;

求既是前缀又是后缀的前缀的可能的长度

           getnext(word) ;           int i ,  j = 0  ;           ans[++j] = i = strlen(word) ;           while(next[i] > 0){               ans[++j] = next[i] ;               i = next[i] ;           }

最小覆盖矩阵

把一整行的字符当做一个字符,r行字符相当于一个字符串,求最小的循环节得到H

暴力统计W

char  word[10008][78] ;int   next[10008] ;int   n , m  ;void  getnext(){      next[0] = -1 ;      int i = 0 ,  j = -1 ;      while(i < n){           if(j == -1 || strcmp(word[i] , word[j]) == 0)                 next[++i] = ++j ;           else  j = next[j] ;      }}int   dp[78] ;int   main(){      int i  , x , y ;      while(scanf("%d%d" ,&n , &m) != EOF){           for(i = 0 ; i < n ; i++) scanf("%s" , word[i]) ;           memset(dp , 0 , sizeof(dp)) ;           for(i = 0 ; i < n ; i++){                for(int j = 1 ; j <= m ; j++){                    for(x = 0 , y = j ; y < m ; x++ , y++){                         if(word[i][x%j] != word[i][y])  break ;                    }                    if(y == m) dp[j]++ ;                }           }           for(i = 1 ; i <= m ; i++) if(dp[i] == n)  break ;           getnext() ;           printf("%d\n" , i * (n - next[n]))  ;      }      return 0 ;}

非暴力统计W
char  s[78] ;int   main(){      int i  , x , y ;      while(scanf("%d%d" ,&n , &m) != EOF){           for(i = 0 ; i < n ; i++) scanf("%s" , word[i]) ;           memset(dp , 0 , sizeof(dp)) ;           for(i = 0 ; i < n ; i++){                strcpy(s , word[i]) ;                for(int j = m-1 ; j > 0 ; j--){                    s[j] = 0 ;                    for(x = 0 , y = 0 ;word[i][y]; x++ , y++){                         if(! s[x]) x = 0 ;                         if(s[x] != word[i][y]) break ;                    }                    if(! word[i][y]) dp[j]++ ;                }           }           for(i = 0 ; i < m ; i++) if(dp[i] == n)  break ;           getnext() ;           printf("%d\n" , i * (n - next[n]))  ;      }      return 0 ;}




0 0
原创粉丝点击