開啟章節選單
1371 Period
題目說明
給兩個字串A,B,可以把B分成好幾份子字串,A與每一段的字串的最短編輯距離的最大值為k,求k的最小值 exp: A="abc" B="abbbcabc" 分割方法為 B->"ab","bbc","abc"(編輯距離分別為 1,1,0 )最大編輯距離為1,這是所有分割法中,能分割出最小的最大值
解題過程
最短編輯距離edit-distance(ED)的dp式與LCS的精神類似 dp式: dp[i][j]:a在前i個字與b在前j個字的ED if a[i-1]==b[i-1]: dp[i][j]=dp[i-1][j-1]如果最後一個字元相同,那麼這個字串的ED就等於加上這個字元前的字串ED前的字串ED else: dp[i][j]=min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])否則就輸出改變次數最少的那個來+1 使用二分搜尋,來搜尋B序列是否可被分割後,最大子序列小於某個數字,如果可以就縮小數字,否則就提高數字 而一開始,可以先將這個數字設為A跟B比較的最大長度,再漸漸用二分搜尋去找。 至於要怎麼去找B序列被分割後會不會大於某數:
假設B有個子區間,開頭為Bs,結尾為Be,某數為m 用動態規劃的方式,bool dp[i]代表B的前i個字的序列的切割編輯距離能不能<=m
然後我們再遍歷B的所有區間: 如果這個區間的切割編輯距離竟然<=m 而且 Bs前面的序列的切割編輯距離(已經動態規劃紀錄的部分)竟然也可以,那麼: Be前面就是可以被切割的(dp[i]=true) 到最後,輸出整個B的切割編輯距離可不可以<=m
#include<iostream> #include<climits> using namespace std; int ED(string a, string b) { int dp[a.length() + 1][b.length() + 1]; for(int i = 0; i <= b.length(); i++) dp[0][i] = i; for(int i = 0;i <= a.length(); i++) dp[i][0] = i; for(int i = 1;i <= a.length(); i++) for(int j = 1;j <= b.length(); j++) if(a[i-1] == b[j - 1]) dp[i][j] = dp[i - 1][j - 1]; else dp[i][j] = 1 + min(min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]); return dp[a.length()][b.length()]; } int isValid(const string& A, const string& B, int maxDist) { int n = B.length(); int dp[n+1]; dp[0] = 1; for(int i = 1; i <= n; i++) { dp[i] = 0; for(int j = 0; j < i; j++) { string part = B.substr(j, i - j); if(ED(A, part) <= maxDist && dp[j]) dp[i] = 1; } } return dp[n]; } int main() { int T; cin >> T; while(T--) { string A, B; cin >> A >> B; int left = 0, right = max(A.length(), B.length()), ans = right; while(left <= right) { int mid = (left + right) / 2; if(isValid(A, B, mid)) { ans = mid; right = mid - 1; } else { left = mid + 1; } } cout << ans << endl; } return 0; }