Aiiage Camp Day3 C Communist

题意

  给两行词串。第二行无重复,第一行有重复。每次可以在第一行删去一个和第二行的公共子序列。问最少几次可以删完。

  长度<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
", i);
60                 break;
61             }
62     }
63     
64     return 0;
65 }
原文地址:https://www.cnblogs.com/aseer/p/8451237.html