轉載請註明出處:http://blog.csdn.net/ns_code/article/details/21043665
今天下午阿里電面的題目,給定一個字符串,輸出其所有連續子串,如:給定字符串爲abcd,則要輸出的其全部連續子串爲:a,b,c,d,ab,bc,cd,abc,bcd,abcd。
很快給出了最簡單的方法,就是先從第一個字符遍歷,向後輸出,再從第二個字符開始遍歷,向後輸出,依此類推,直到開始遍歷的字符爲數組的最後一個字符。這個時間複雜度很高啊,要O(n*n*n)。
接下來就假設字符串很大,想優化的方法,不知道腦子是短路了還是咋地,居然聯想到Trie樹上去了,完全不沾邊的東西。電面後想了下,感覺應該用遞歸,吃完飯,去圖書館用紙畫了下,就是遞歸(還是A題A的少啊!)。後來寫出來代碼感覺貌似木有提高時間複雜度哦!
先貼出來吧,首先是最簡單的遍歷法,時間複雜度爲O(n*n*n)
[cpp] view plaincopy
- /*
- 蠻力求解法
- */
- void AllSubstring1(const char *str,int high)
- {
- int i, j, k;
- if(!str)
- return;
- for(i=0;i<=high;i++)
- {
- for(j=i; j<=high;j++)
- {
- for(k = i; k<=j; k++)
- printf("%c", str[k]);
- printf("\t");
- }
- printf("\n");
- }
- }
而後我又減少了一層循環,增加了一個遞歸操作,如下:
[cpp] view plaincopy
- /*
- 遞歸求解法
- */
- void AllSubstring2(const char *str,int low,int high)
- {
- int i;
- int cur = high;
- if(!str)
- return;
- while(low <= cur)
- {
- for(i=low;i<=cur;i++)
- printf("%c",str[i]);
- printf("\t");
- cur--;
- }
- printf("\n");
- if(low<high)
- {
- AllSubstring2(str,low+1,high);
- }
- }
但是感覺時間複雜度沒有提高啊(分治策略的遞歸代替循環,可以將時間複雜度從O(n)降到O(logn),當然是每次將問題減到一半分治策略,若果是類似斐波那契序列的分治,效果更差。每層遞歸只減少一個元素的遞歸無法降低時間複雜度)!我在紙上大致算了下。遞歸求解的時間複雜度按如下公式推導:
T(n) = T(n-1) + n*n
這樣應該得到:
T(n) = n*n + (n-1)*(n-1) +...+2*2 + 1 = n(n+1)(2n+1)/6
時間複雜度還是O(n*n*n)!!
而且感覺還可以用一個遞歸來替代上面的一層循環,沒時間想了,圖書館要關門了,還要去跑步,明天又要跟導師彙報,暫時先擱一擱吧!忙完了再回來思考下,大家有好的思路的話,希望在下面貼出來!
後續
沒想到發生來一天不到,那麼多人回覆,很感謝大家的熱心思考啊!而且看時間有些人是熬夜寫的代碼啊,不由心生佩服,不過希望要多注意身體哈。
很多人都採用瞭如下代碼來進行優化(我稍微做了些改動,測試通過)
[cpp] view plaincopy
- /*
- 循環輸出子串
- */
- void AllSubstring3(const char *str,char *arr)
- {
- //穿入的arr的長度爲len+1,最後一個位置保存'\0'
- int i,j;
- unsigned int len = strlen(str);
- strcpy(arr,str);
- for(i=len-1;i>=0;i--)
- {
- arr[i+1] = '\0';
- for(j=i;j>=0;j--)
- printf("%s\t",&arr[j]);
- printf("\n");
- }
- }
採用直接打印子串的方法,減少了一個循環,18樓說的在理,這樣只是代碼間簡潔了些,但直接輸出字符串和諸葛輸出字符效果是一樣的,因此複雜度並沒有降低。
假設沒有重複的情況,連續子串的數目爲sum = n + n-1 + n-2 +...+2+1 = n(n-1)/2,但是因爲基本的操作是輸出單個字符,因此輸出次數也就達到了sum*n的級別了,時間複雜度最優貌似也就是O(n*n*n)了,這樣看來貌似就沒什麼可優化的餘地嘍!!求解。。
再次感謝大家的思考和回覆,最近忙死鳥,不能一一細看和回覆,望見諒!!