大話數據結構之四(串)

串的定義

串是由零個或多個字符組成的有限序列,又名叫字符串算法

串中的字符數目n稱爲串的長度數組

零個字符的串稱爲空串函數

串的抽象數據類型

串的順序存儲結構

串我鏈式存儲結構

一個結點能夠存儲一個字符也能夠考慮存儲多個字符,最後一個結點如果未被佔滿時,能夠用#或其它非串值字符補全google

樸素的模式匹配算法

對主串的每個字符做爲子串開頭,與要匹配的字符進行匹配。對主串作大循環,每一個字符開頭作T的長度的小循環,直到匹配成功或所有遍歷完成爲止。spa

時間複雜度爲O(n+m)3d

/* 返回子串T在主串S中第pos個字符以後的位置。若不存在,則函數返回值爲0。 */
/* 其中,T非空,1≤pos≤StrLength(S)。 */
int Index(String S, String T, int pos) 
{
    int i = pos;    /* i用於主串S中當前位置下標值,若pos不爲1,則從pos位置開始匹配 */
    int j = 1;                /* j用於子串T中當前位置下標值 */
    while (i <= S[0] && j <= T[0]) /* 若i小於S的長度而且j小於T的長度時,循環繼續 */
    {
        if (S[i] == T[j])     /* 兩字母相等則繼續 */
          {
            ++i;
             ++j; 
          } 
          else                 /* 指針後退從新開始匹配 */
          {  
             i = i-j+2;        /* i退回到上次匹配首位的下一位 */
             j = 1;             /* j退回到子串T的首位 */
          }      
    }
    if (j > T[0]) 
        return i-T[0];
    else 
        return 0;
}

利用上面的算法,假設咱們要從主串goodgoogle中找到google,則須要下面的步驟指針

想一想若是咱們要在主串S="00000000000000000000000000000000000000000000000000001",而要匹配的子串T=「0000000001」code

也就是說T串須要在S串的前40個位置都須要判斷10次並獲得不匹配的結論,直到第41位才所有匹配相等blog

 

所以最壞的狀況的時間複雜度爲O(((n-m)+1)*m)字符串

KMP模式匹配算法原理

若是主串S="abcdefgab",要匹配的子串T="abcdex"

若是用樸素算法的話,則匹配的流程圖以下所示:

細想一下,子串T中「abcdex」 首字母a與後面的串「bcdex」中的任意一個字符都不相等,既然a不與本身後面的子串中任何一個字符相等,那麼對於上圖1來講,前五個字符分別相等,意味着子串T的首字符a不可能與s串的第2位到第5位的字符相等,也就是說在上圖中二、三、四、5的判斷都是多餘的。

若是子串T中有與首字符相等的字符,也是能夠省略一部分沒必要要的判斷步驟的。

咱們把T串各個位置的j值的變化定義爲一個數組next,那麼next的長度就是T串的長度

舉例說明next數組值推導

KMP模式匹配算法實現代碼

/* 經過計算返回子串T的next數組。 */
void get_next(String T, int *next) 
{
    int i,j;
      i=1;
      j=0;
      next[1]=0;
      while (i<T[0])  /* 此處T[0]表示串T的長度 */
     {
        if(j==0 || T[i]== T[j])     /* T[i]表示後綴的單個字符,T[j]表示前綴的單個字符 */
        {
              ++i;  
            ++j;  
            next[i] = j;
        } 
        else 
            j= next[j];    /* 若字符不相同,則j值回溯 */
      }
}

/* 返回子串T在主串S中第pos個字符以後的位置。若不存在,則函數返回值爲0。 */
/*  T非空,1≤pos≤StrLength(S)。 */
int Index_KMP(String S, String T, int pos) 
{
    int i = pos;        /* i用於主串S中當前位置下標值,若pos不爲1,則從pos位置開始匹配 */
    int j = 1;            /* j用於子串T中當前位置下標值 */
    int next[255];        /* 定義一next數組 */
    get_next(T, next);    /* 對串T做分析,獲得next數組 */
    while (i <= S[0] && j <= T[0]) /* 若i小於S的長度而且j小於T的長度時,循環繼續 */
    {
        if (j==0 || S[i] == T[j])     /* 兩字母相等則繼續,與樸素算法增長了j=0判斷 */
          {
             ++i;
             ++j; 
          } 
          else             /* 指針後退從新開始匹配 */
               j = next[j];/* j退回合適的位置,i值不變 */
    }
    if (j > T[0]) 
        return i-T[0];
    else 
        return 0;
}

上面get_next的時間複雜度爲O(m),而index_KMP中while循環的時間複雜度爲O(n),因此整個算法的時間複雜度爲O(n+m)

KMP模式匹配算法改進

好比主串S="aaaabcde",子串T="aaaaax",那麼next數組值分別爲012345

利用KMP算法比較的過程以下圖所示:

當i=5,j=5時,b與a不相等,如上圖1

j=next[5]=4,如上圖2,b與第四個位置的a依然不等

j=next[4]=3,如上圖3,...

細想一下,二、三、四、5步驟都是多餘的,由於T串的第2、3、4、五位置的字符都與首位a相等,那麼能夠利用首位的next[1]的值去取代與它相等的字符後續的next[j]的值

改良版的KMP算法實現代碼

/* 求模式串T的next函數修正值並存入數組nextval */
void get_nextval(String T, int *nextval) 
{
      int i,j;
      i=1;
      j=0;
      nextval[1]=0;
      while (i<T[0])  /* 此處T[0]表示串T的長度 */
     {
        if(j==0 || T[i]== T[j])     /* T[i]表示後綴的單個字符,T[j]表示前綴的單個字符 */
        {
              ++i;  
            ++j;  
            if (T[i]!=T[j])      /* 若當前字符與前綴字符不一樣 */
                nextval[i] = j;    /* 則當前的j爲nextval在i位置的值 */
              else 
                nextval[i] = nextval[j];    /* 若是與前綴字符相同,則將前綴字符的 */
                                            /* nextval值賦值給nextval在i位置的值 */
        } 
        else 
            j= nextval[j];            /* 若字符不相同,則j值回溯 */
      }
}

 

nextval數組值推導

(具體分析圖以下所示:

另一個例子(看看你推導正確了沒)

相關文章
相關標籤/搜索