【演算法拾遺】阿里實習生電面題目:輸出給定字串的全部連續子串

蘭亭風雨發表於2014-03-12

轉載請註明出處: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)

/*
蠻力求解法
*/
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");
	}
}

    而後我又減少了一層迴圈,增加了一個遞迴操作,如下:

/*
遞迴求解法
*/
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)!!

    而且感覺還可以用一個遞迴來替代上面的一層迴圈,沒時間想了,圖書館要關門了,還要去跑步,明天又要跟導師彙報,暫時先擱一擱吧!忙完了再回來思考下,大家有好的思路的話,希望在下面貼出來!


後續

    沒想到發生來一天不到,那麼多人回覆,很感謝大家的熱心思考啊!而且看時間有些人是熬夜寫的程式碼啊,不由心生佩服,不過希望要多注意身體哈。

    很多人都採用瞭如下程式碼來進行優化(我稍微做了些改動,測試通過)

/*
迴圈輸出子串
*/
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)了,這樣看來貌似就沒什麼可優化的餘地嘍!!求解。。

    再次感謝大家的思考和回覆,最近忙死鳥,不能一一細看和回覆,望見諒!!


相關文章