アルゴリズムノート(1)-KMPアルゴリズムの実践に関する質問

コンテンツ

1.strStrを実装します

2.繰り返される部分文字列 


1.strStrを実装します

解決策1:ブルートフォースマッチング(BF)アルゴリズム

int strStr(char * haystack, char * needle){
    assert(haystack!=NULL&&needle!=NULL);
    int len1=strlen(haystack);
    int len2=strlen(needle);
    int i=0,j=0;
    if(len2==0)
    {
        return 0;
    }
    if(len1==0&&len2!=0)
    {
        return -1;
    }
    while(i<len1&&j<len2)
    {
      if(haystack[i]==needle[j])
      {
          i++;
          j++;
      }
      else{
          i=i-j+1;
          j=0;
      }
    }
    if(j>=len2)
    {
        return i-j;
    }
    else{
        return -1;
    }
}

解決策2:KMPアルゴリズム

int strStr(char * haystack, char * needle){
    if(needle[0]=='\0')
    return 0;
      int len1=(int)strlen(haystack);
      int len2=(int)strlen(needle);
      int i=0;
      int j=0;
      int ans=-1;
      int* next=(int*)malloc(sizeof(int)*len2);
      getnext(next,needle);
      while(i<len1)
      {
          if(j==-1||haystack[i]==needle[j])
          {
             ++i;
              ++j;
          }
          else{
              j=next[j];
          }
          if(j==len2)
      {
          ans=i-len2;
          break;
      }
      }
      
          return ans;
}
void getnext(int next[],char* needle)
{
    next[0]=-1;
    int j=0;
    int k=-1;
    int len=(int)strlen(needle);
    while(j<len-1)
    {
        if(k==-1||needle[j]==needle[k])
        {
            ++j;
            ++k;
            next[j]=k;
        }
        else{
            k=next[k];
        }
    }

}

 ソリューション1とソリューション2の関連する知識ポイントを理解していない場合は、このブログを読んで詳細な説明を参照してください。

>>>KMPアルゴリズムの詳細な説明

解決策3:ハッシュが暴力を加速する

class Solution {
public:
    int strStr(string haystack, string needle) {
        if(needle=="") return 0;
        int n = haystack.size(), m = needle.size();
        int hash = 0;
        for(auto c: needle){
            hash += c -'a';
        }
        int cur = 0;
        for(int i = 0; i < n; i++){
            cur += haystack[i] - 'a';
            if(i >= m ) cur -= haystack[i-m] - 'a';
            if(i >= m-1 && cur == hash && haystack.substr(i-m+1,m) == needle)
                return i-m+1;
            
            
        }
        return -1;
    }
};

解決策4:

問題解決のアイデア1.2
つのポインターを定義します。ヘッドポインターは干し草の山の配列の頭から始まり、テールポインターの位置は、針の配列の長さ(0 + len2-1)
2によって決まります。 whileループに入ると、ループの終わりの条件は次のようになります。テールポインタがhaystack配列の終わりに到達します
。3。haystack配列がheadで始まり、長さがlen2のサブストリングがneedle配列と同じかどうかを判断します。 。その場合は、この時点でヘッド値を返します
。4.ヘッドポインタとテールポインタで++操作を実行し、ウィンドウサイズを維持します
。5。ループが終了します。これは、マッチングが失敗したことを意味し、-1を返します。

class Solution {
public:
    int strStr(string haystack, string needle) {
        int len1=haystack.size(),len2=needle.size();
        //尾指针的位置由needle数组的长度确定,也就是(0+len2-1)
        int head=0,tail=len2-1;
        //循环结束的条件是尾指针到达haystack数组的尾部
        while(tail<len1){
            //判断haystack数组以head为开头,len2为长度的子串和needle数组是否相同
            if(haystack.substr(head,len2)==needle) return head;
            head++;
            tail++;
        }
        return -1;
    }
};

解決策5:C++ライブラリ関数の使用

class Solution {
class Solution {
public:
    int strStr(string haystack, string needle) {
        if(needle.empty())
            return 0;
        int pos=haystack.find(needle);
        return pos;
    }
};

この問題には多くの解決策があるはずです。考えれば、コメント欄にメッセージを残して一緒に話し合うことができます。

2.繰り返される部分文字列 

解決策1:KMPアルゴリズム

以下は、私がLikouで見つけたよく理解されているKMPソリューションです。

次の配列が1つ減らず、移動しない方法。詳細については、
コードを参照してください。
次の配列を印刷して、次の配列のルールを確認することを強くお勧めします。これは、理解に役立ちます。 KMPアルゴリズム。

最終的なif判定は、次のように理解できます。
答えがtrueの場合、次のテーブルの最初の数桁(サブストリング)はすべて0であり、次は常にインクリメントされます。Next(n-1)は元のテーブルを格納します。文字列からサブ文字を引いたもの。文字列の長さの値はlen2として記録され、len-len2の値はlen1として記録されます。len1は部分文字列の長さであり、lenで割り切れる必要があります。

 

//求字符串s的next数组
void Getnext(int *next, char *s, int len)
{
    int j=0;
    next[0]=0;
    for(int i=1; i<len; i++)
    {
        while(j>0 && s[i]!=s[j])
        {
            j=next[j-1];
        }
        if(s[i] == s[j])
        {
            j++;
        }
        next[i]=j;
    }
}

bool repeatedSubstringPattern(char * s)
{
    int len = strlen(s);
    if(len == 0) return false;
    int *next = (int*)malloc(sizeof(int) * len);

    Getnext(next, s, len);

    // next[len-1]!=0 代表s字符串有最长的相等前后缀
    // len % (len - next[len-1]) == 0 
    // 代表(数组长度 - 最长相等前后缀的长度)正好可以被数组的长度整除
    if(next[len-1]!=0 && len % (len - next[len-1]) == 0)
    {
        return true;
    }
    return false;
}

解決策2:列挙メソッド

アイデアとアルゴリズム

長さnnの文字列ssが、長さn'nの部分文字列s's'で構成され、何度も繰り返される場合、nnはn'n'の倍数である必要があります。s's'はssの接頭辞である必要があります。 \ in [n'、n)i∈[n'、n)、s [i] = s [in'] s [i] = s [i−n']があります。つまり、ssの長さn'n'の接頭辞はs's'であり、その後の各位置の文字s [i] s [i]は、その前のn'n番目の文字と同じである必要があります。文字s[in']s [i−n']は同じです。したがって、n'n'を小さいものから大きいものへと列挙し、文字列ssをトラバースして上記の判断を下すことができます。小さな最適化では、部分文字列を少なくとも1回繰り返す必要があるため、n'n'はnnの半分より大きくなることはなく、[1、\ frac{n}{2}の範囲内にある必要があるだけであることに注意してください。 ] [1、2n]n'n'を列挙するだけで十分です。

bool repeatedSubstringPattern(char * s){
    int len=strlen(s);
   for(int i=1;i*2<=len;i++)
   {
       if(len%i==0){
       bool match=true;
       for(int j=i;j<len;j++)
       {
           if(s[j]==s[j-i])
           {
            match=true;
           }
           else{
               match=false;
               break;
           }
       }
       if(match==true)
       {
           return true;
       }
   }
   }
       return false;
}

解決策3:文字列照合

bool repeatedSubstringPattern(char* s) {
    int n = strlen(s);
    char k[2 * n + 1];
    k[0] = 0;
    strcat(k, s);
    strcat(k, s);
    return strstr(k + 1, s) - k != n;
}


 

 

 

おすすめ

転載: blog.csdn.net/m0_58367586/article/details/123094572