[字符串]最長不重復子串


題目描述:

   最長不重復子串(Longest No Repeat String,LNRS)就是從一個字符串中找到一個連續子串,該子串中任何兩個字符都不能相同,且該子串的長度是最大的。

分析:

解法一:動態規划

  動態規划就是用來解決這種最優化問題,關於字符串的很多有趣的問題如最長公共自序列,最長上升子序列等都可以用動態規划來解,這道題我的第一想法也是動態規划。

  動態規划的核心在於尋找最優子結構,對於一個字符,如果他與他前面的最長不重復子串都沒有相同的字符,那么他也可以加入這個子串中,構成一個新的子串。即對於字符數組a[],dp[i]表示以a[i]為結尾的最長不重復子串長度,dp[0] = 1,最后取dp中的最大值即可。代碼如下:

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <string.h>
 4 
 5 using namespace std;
 6 char in[10001];
 7 
 8 int dp[10001];
 9 
10 int LNRS_dp(char *in)
11 {
12     int i, j;
13     int len = strlen(in);
14     int last = 0;     // 上一次最長子串的起始位置
15     int maxlen,maxindex;
16     maxlen = maxindex = 0;
17  
18     dp[0] = 1;
19     for(i = 1; i < len; ++i)
20     {
21         for(j = i-1; j >= last; --j) // 遍歷到上一次最長子串起始位置
22         {
23             if(in[j] == in[i])
24             {
25                 dp[i] = i - j;
26                 last = j+1; // 更新last_start
27                 break;
28             }else if(j == last) // 無重復
29             {
30                 dp[i] = dp[i-1] + 1;//長度+1 
31             }
32         }
33         if(dp[i] > maxlen)
34         {
35             maxlen = dp[i];
36         }
37     }
38     return maxlen;
39 }
40 
41 int main()
42 {
43      freopen("1530.in","r",stdin);
44      freopen("1530.out","w",stdout);
45      
46      while(scanf("%s",in)!=EOF)
47      {
48           printf("%d\n",LNRS_dp(in));
49      }
50      return 0;
51 }
View Code

  顯然這個方案是O(n2)的復雜度,且對字符種類沒有限制。

解法二:Hash

  很多情況下,在描述一個字符串時,都是英文字母組成的字符,因此可能出現的元素是有限的(26個),因此就有了第二種想法,Hash。

  這種想法在於使用一個大小為26的數組visited[]記錄每個字符是否出現,然后枚舉最長不重復子串的起點。代碼如下:

 1 void LNRS_hash(char*  s)
 2 {
 3     char visited[26];
 4     int i, j;
 5     int maxlen = 0;
 6     int maxIndex;
 7     int  len = strlen(s);
 8 
 9     memset(visited, -1, 26);
10     for (i = 0; i < len; i++)
11     {
12         visited[s[i] - 'a'] = 1;
13         for(j = i+1 ; j < len; j++)
14         {
15             if(visited[s[j]-'a'] == -1) //該字符沒有出現
16             {
17                 visited[s[j]-'a'] = 1;
18                 if(j - i+1> maxlen)
19                 {
20                     maxlen = j - i+1;
21                     maxIndex = i;//最長不重復子串的起點
22                 }
23             }
24             else
25                 break;
26         }
27         memset(visited, -1, 26);
28     }
29     printf("%d\n", maxlen);
30 }
View Code

  這也是O(n2)的復雜度。僅適用於字符種類明確的情形。

解法三:動態規划的改進

    有了第二種Hash的思想,我們是不是也可以考慮對第一種動態規划的方法進行針對性改造呢?

  回顧之前動態規划的解法,枚舉每一個以a[i]為結尾的最長不重復子串,將a[i]與之前的子串元素一一比較,判斷是否出現過。很顯然,在字符種類有限的情況下(如只有字母組成的字符串),這種判重可以用Hash來實現。代碼如下:

 1 int LNRS_dp(char *in)
 2 {
 3     int i, j;
 4     int len = strlen(in);
 5     int last = 0;     // 上一次最長子串的起始位置
 6     int maxlen;
 7      char visited[26];
 8      memset(visited, -1, sizeof(visited));
 9     dp[0] = 1;
10     visited[in[0]-'a'] = 0;
11     maxlen = 1; 
12     for(i = 1; i < len; ++i)
13     {
14         if(visited[in[i]-'a'] == -1 || visited[in[i]-'a']<last)//未訪問過,或者是之前訪問的 
15         {
16              dp[i] = dp[i-1] + 1;
17              visited[in[i]-'a'] = i; 
18         }
19         else
20         {
21               dp[i] = i - visited[in[i]-'a'];
22               last = visited[in[i]-'a']+1;
23               visited[in[i]-'a'] = i;
24         }
25         if(dp[i] > maxlen)
26         {
27             maxlen = dp[i];
28         }
29     }
30     return maxlen;
31 }
View Code

  PS:這是一個OJ的題,http://ac.jobdu.com/problem.php?pid=1530,前兩種方法可以過,第三種方法過不了,求大神指導下,第三種方法哪里沒有考慮到,多謝!

  有網友給出了一個更簡潔的解法,思想與解法三是類似的,代碼如下:

 1 #include <cstdio>
 2 #include <cstring>
 3   
 4 int main()
 5 {
 6     char s[10001];
 7     while (scanf("%s", s) != EOF)
 8     {
 9         int p[128];
10         for (char c = 'a'; c <= 'z'; ++c)
11         {
12             p[c] = -1;
13         }
14   
15         int n = strlen(s), right = -1, answer(0);
16         for (int i = 0; i < n; ++i)
17         {
18             if (p[s[i]] > right)
19             {
20                 right = p[s[i]];
21             }
22             p[s[i]] = i;
23   
24             if (i - right > answer)
25             {
26                 answer = i - right;
27             }
28         }
29   
30         printf("%d\n", answer);
31     }
32   
33     return 0;
34 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM