【算法题】17.字符串匹配

148 阅读3分钟

题目

有主串 S="abcacabdc",模式串T="abd",请找出模式串在主串中第一次出现的位置。提示:主串和模式串均为小写字母且都是合法输入。

示例1

输入: S = "abcacabdc",T = "abd"
输出: 5

BF算法

思路

  1. 遍历主串,若主串的字符 等于 模式串的第一个字符,开始之后的是否与模式串相等,记录相等字符个数 range
  2. range 等于模式串长度,则跳出循环,返回第一个字符索引
  3. range 不等于模式串长度,则直接将遍历索引 i+ range - 1 进行跳跃遍历。

复杂度

时间复杂度: O(N)O(N)

空间复杂度: O(1)O(1)

代码实现

int indexAt(char *s,char *subs){
      int i = 0;
         int result = -1;
      while(s[i] != '\0'){
         if (s[i] == subs[0])
         {
            int index = i;
            index++;
            int range = 1;
            while(subs[index - i] != '\0' && s[index] == subs[index - i]){
                  printf("%c\n", subs[index - i]);
                  range++;
                  index++;
            }
            if (range  == strlen(subs))
            {
               result = i;
               break;
            }else{
                  i = i+range - 1;
            } 
         }
         i++; 
      }
      return result;
}

Sunday算法

思路

  1. 若主串的字符等于模式串的第一个字符,开始之后的是否与模式串相等,记录相等字符个数 range
  2. range 等于模式串长度,则跳出循环,返回第一个字符索引
  3. range 不等于模式串长度,判断 i + TLen 是否小于主串长度,若小于则判断 (i + TLen) 处的字符串是否出现在模式串中(从右到左比较),若出现 则( i = i + 所在模式串中的位置)。若未出现,则i = (i+ TLen).
  4. 继续迭代

复杂度

时间复杂度: O(N)O(N)

空间复杂度: O(1)O(1)

代码实现

int strStr(char * S, char * T){
   int SLen  = strlen(S);
   int TLen = strlen(T);

      if( SLen < TLen) {
         return -1;
      }

      if(T == 0){
         return 0;
      }

      int result = -1;

      int i = 0;
      
      while(i < SLen){
      if (S[i] == T[0])
      {
      int temp = 1;
      while(temp < TLen){
         if (S[i + temp] == T[temp])
               {
               temp++;
               }else{
                  break;
               }
      }
      if (temp == TLen)
            {
            result = i;
            break;
            }
      }

      int temp = TLen - 1;
      if (i + TLen < SLen)
      {
         while(temp > 0){
            if (S[i + TLen] == T[temp])
         {
         break;
         }
         temp--;
      }

         i = (TLen- temp ) + i;
      }else{
         break;
      }
      
      
      } 
      return result;

}

KMP算法

介绍

    KMP 算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个 next() 函数实现,函数本身包含了模式串的局部匹配信息。

    例:模式串T ABABAA,在已经匹配的模式串子串中,找出最长的相同的前缀和后缀的字符个数记录在 Next 数组相应位置。

模式串TABABAA
索引012345
Next数组001231

步骤

  • 求导 Next 数组

    1. 声明两个变量索引 k 与 临时记录相同前缀与后缀字符个数 j, next[0] = 0
    2. k1<k<TLen1 < k < TLen 内做递增循环。当 T[j] != T[k] 并且 j > 0 时, j = next[j - 1],直到 j = 0 || T[j] = T[k] 循环截止;
    3. T[j] = T[k] 时, j++;
    4. next[k] = j
  • 根据 Next 数组信息,进行主串与模式串的匹配。

    1. 声明两个变量 ij,分别记录主串 S 访问索引与模式串 T访问索引;
    2. i0<i<SLen0 < i < SLen 内递增循环。当 j > 0 并且 S[i]!=T[j] 时 , j = next[j - 1],直到 j = 0 || S[i] = T[j] 循环截止;
    3. S[i] = T[j] 时, j++;
    4. j = TLen, 已匹配到对应字符串,否则返回 -1

复杂度

时间复杂度: O(M+N)O(M+N)

空间复杂度: O(N)O(N)

代码实现

   int *getKMPNext(char *T) {

      int Tlen = strlen(T);
      int *next = (int *)malloc(sizeof(int) * Tlen);
      next[0] = 0;
      int temp = 0;
      for (int k = 1; k < Tlen; k++) {
         while (temp > 0 && T[temp] != T[k]) {
               temp = next[temp - 1];
         }

         if (T[temp] == T[k]) {
               temp++;
         }
         next[k] = temp;
      }
      return next;
   }
   int strStr(char *S, char *T) {
      int SLen = strlen(S);
      int TLen = strlen(T);
      if (TLen == 0) {
         return 0;
      }
      int *next = getKMPNext(T);

      for (int i = 0, j = 0; i < SLen; i++) {
         while (j > 0 && S[i] != T[j]) {
               j = next[j - 1];
         }
         if (S[i] == T[j])
               j++;
         if (j == TLen)
               return i - j + 1;
      }
      return -1;
   }