题意
给两行词串。第二行无重复,第一行有重复。每次可以在第一行删去一个和第二行的公共子序列。问最少几次可以删完。
长度<1e5 26小写字母
题解
每次求最长公共子序列显然会T。
等价问题是,对第二行词从小到大标号,第一行上升子序列个数。
那么只需要倒序扫第一行词串。初始序列数为0,记录每个序列当前最小值,初始为∞。若当前扫到的值大于等于每个序列当前最小值,则需要一个新的序列。否则更新第一个最小值比其大的序列的最小值。这个操作可以二分完成,总复杂度O(nlogn)。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 vector<string> a, d; 5 map<string, int > h; 6 int f[100010]; 7 8 int main() 9 { 10 int T; 11 scanf("%d", &T); 12 getchar(); 13 while (T--) 14 { 15 a.clear(); 16 d.clear(); 17 h.clear(); 18 string s1, s2, s; 19 getline(cin, s1); 20 getline(cin, s2); 21 stringstream ss(s1); 22 while (ss >> s) 23 a.push_back(s); 24 stringstream sss(s2); 25 while (sss >> s) 26 d.push_back(s); 27 28 for (int i = 0; i < d.size(); ++i) 29 h[d[i]] = i; 30 31 for (int i = 0; i <= a.size(); ++i) 32 f[i] = 100010; 33 for (int i = a.size() - 1; i >= 0; --i) 34 { 35 int x = h[a[i]]; 36 int l(0), r(a.size()); 37 while (l < r) 38 { 39 int mid = (l + r) / 2; 40 if (f[mid] > x && mid == 0) 41 { 42 f[mid] = x; 43 break; 44 } 45 if (f[mid] <= x && f[mid + 1] > x) 46 { 47 f[mid + 1] = x; 48 break; 49 } 50 if (f[mid] <= x) 51 l = mid + 1; 52 else 53 r = mid; 54 } 55 } 56 for (int i = 0; i <= a.size(); ++i) 57 if (f[i] == 100010) 58 { 59 printf("%d\n", i); 60 break; 61 } 62 } 63 64 return 0; 65 }