97. Interleaving String
Given a list of unique words. Find all pairs of distinct indices(i, j)
in the given list, so that the concatenation of the two words, i.e.words[i] + words[j]
is a palindrome.
Example 1:Givenwords
=["bat", "tab", "cat"]
Return[[0, 1], [1, 0]]
The palindromes are["battab", "tabbat"]
Example 2:Givenwords
=["abcd", "dcba", "lls", "s", "sssll"]
Return[[0, 1], [1, 0], [3, 2], [2, 4]]
The palindromes are["dcbaabcd", "abcddcba", "slls", "llssssll"]
思考
- 理解題意
這道題目的特點是要在兩個字符串s1、s2里面輪流找slice,交織拼接成s3
注意的地方是sclice必須是連續的,比如下面的情況就不合理:
s1 : sliceA, sliceB, sliceC
s2 : sliceD, sliceE
s3 = A D C E B, 因為這里雖然滿足了 s1 s2的slice是輪流出現的,
但是對于s1 來說,slice出現的次序是 : ACB, 不是順序的ABC,所以不滿足“交織”這個概念
正確的一個列子是:s3 = A D B E C
思路
- 怎么滿足“交替出現”---> 2 個方向
- 找得到的時候怎么樣?找不到的時候怎么樣?
(1)回溯 ?(back until true, and restart again)
(2)DP ?(go froward because you know you are the outcome of your previous ones)
策略
- 回溯的思路【較為直觀,實現繁瑣,輔助變量較多】
- 兩個指針,代表在s1, s2中移動到下一個待匹配字符的位置
- 因為是輪流出現,所以需要一個 count 用奇偶來表示whose turn
- 找不到的話需要退回去,所以需要一個path來記錄前面的拼接方式
- 加入“貪心”的小策略,在s1或者s2中單獨找的時候,是一直找到最長的那個匹配字符,如果不行,需要回退的時候,長度-1,如果長度已經是1了,就需要在path中彈出。這個過程需要更新count
- dp的思路【相對抽象,更加簡潔,快】
- “兩個方向” --> matrix 的兩個維度
- 要在s1,s2中輪流找 -->
if( (str1[i-1]==str3[i+j-1] && map[i-1][j]==true) ||
(str2[j-1]==str3[i+j-1] && map[i][j-1]==true) )
map[i][j] = true;
// 正因為是交替出現,所以str1和str3匹配的時候,
// 要看前一個str2是不是和str3匹配。反之亦然。
- 如何“記憶化”搜索?
- 怎么提高速度?
- 剪枝?
- 2 列代替整個矩陣
- 編程語言上的優化技巧
- 要注意的地方
dp[len1+1][len2+1], 索引str1和str2的時候要記得-1
實現
具體代碼
public boolean isInterleave(String s1, String s2, String s3) {
if(s1==null || s2==null || s3==null)
return false;
if(s1.length() + s2.length() != s3.length())
return false;
int len1 = s1.length();
int len2 = s2.length();
boolean[][] map = new boolean[len1+1][len2+1];
map[0][0] = true;
char[] str1 = s1.toCharArray();
char[] str2 = s2.toCharArray();
char[] str3 = s3.toCharArray();
for(int i=1; i<len1+1; i++)
{
if(str1[i-1]==str3[i-1] && map[i-1][0]==true )
map[i][0] = true;
else
break;
}
for(int j=1; j<len2+1; j++)
{
if(str2[j-1]==str3[j-1] && map[0][j-1]==true)
map[0][j] = true;
else
break;
}
for(int i=1; i<len1+1; i++)
{
for(int j=1; j<len2+1; j++)
{
if( (str1[i-1]==str3[i+j-1] && map[i-1][j]==true) ||
(str2[j-1]==str3[i+j-1] && map[i][j-1]==true) )
map[i][j] = true;
}
}
return map[len1][len2];
}
優化
- 怎么提高速度?
- 剪枝?
@ wait for implement - 2 列代替整個矩陣
@ wait for implement - 編程語言上的優化技巧
(1) 先把 String 轉成 char[], 以后每次操作都對char[]進行,從9ms到8ms
(2) 利用boolean[][] 初始化的時候默認為false, 所以初始化的時候一旦不是true就break, 另外就是對map[i][j]更新的時候只對true進行更新
- 剪枝?
小收獲
- 多種可能性的路徑探索,如果能夠記憶化,就不一定要回溯
- 兩個方向進行,可以抽象成矩陣的兩個維度,或許會比用兩個指針更方便。
To do list
- dp還有很多可以優化的地方,要繼續總結。
- 嘗試把以前做過的dp題都進行優化。
參考: http://blog.csdn.net/u011095253/article/details/9248073
http://www.cnblogs.com/boring09/p/4239255.html
http://46aae4d1e2371e4aa769798941cef698.devproxy.yunshipei.com/sunliymonkey/article/details/48165711