求公共子字符串問題(連續的)
這個題目是當時遠景能源公司現場筆試的一道題目,當時根本就不知道動態規划是什么鬼,直接上來就暴力求解,面試官很諂媚的問我,你這能求出來嗎?當時很年輕的說,能啊!現在想,當時哪來的自信和逗比勇氣說這大話。。。在《進軍硅谷》這本書上看到原題,我是懵逼,怎么想出這種解答出來的,下面直接上思路和代碼。
思路:
定義二維數組dp[i][j]記錄最大公共子串的長度,
- 若要返回字符串可以用s1.substring(i-dp[i][j]+1, i+1)
- 當s[i]==s[j]時,dp[i][j]=dp[i-1][j-1]+1;
- 當s[i]!=s[j]時,dp[i][j]=0;
有點類似於數學歸納法
方案:
- 首先考慮空或者長度為0的情況,直接返回"";
- 然后進入雙重循環:
- 1.利用charAt(int index)方法來比較兩個字符串相等的時機
- 2.考慮邊界情況,兩個字符串中有一個是起始為0就相等,則dp[i][j]=1
- 3.除了邊界情況,其他最大字符串長度為dp[i][j]=dp[i-1][j-1]+1;
- 4.不斷的替換掉最大的長度並返回公共子串
- 最后循環結束后,返回最大的公共子串
1 public static String maxCommonString(String s1, String s2) { 2 String res = ""; 3 if (s1 == null || s1.length() == 0 || s2 == null || s2.length() == 0) 4 return res; 5 int max = 0, m = s1.length(), n = s2.length(); 6 int[][] dp = new int[m][n]; // 定義一個二維數組記錄最大公共子串的長度 7 // 計算到s1的第i個字符和s2的第j個字符為止的最大公共子串長度 8 for (int i = 0; i < m; i++) { 9 for (int j = 0; j < n; j++) { 10 // 如果s1字符串在i處和s2字符串在j處有字符相同,進入if代碼塊中 11 if (s1.charAt(i) == s2.charAt(j)) { 12 if (i == 0 || j == 0) 13 dp[i][j] = 1;// 邊界的情況 14 else 15 dp[i][j] = dp[i - 1][j - 1] + 1;// 加上當前長度 16 // 記錄最大長度和子串 17 if (dp[i][j] > max) { 18 max = dp[i][j]; 19 res = s1.substring(i - dp[i][j] + 1, i + 1);// substring()左閉右開 20 } 21 } 22 } 23 } 24 return res; 25 }
動態規划介紹
動態規划算法一般是基於一個遞推公式(如上面的當s[i]==s[j]時,dp[i][j]=dp[i-1][j-1]+1;)以及一個或多個初始狀態(當s[i]!=s[j]時,dp[i][j]=0;),當前子問題其實是由上一次子問題的解推算出來的。
【附帶福利:markdown每行縮進的方式】
半方的空白&ensp;或 ;
全方的空白&emsp;或 ;
不斷行的空白格 ;或 ;
(分號都是英文格式的)