• 【???】今天上午的考试题——区间dp和字符串/线性筛的综合应用


      T3还没有打出来,就先放两道。

    ----------------------------------------------------------

    T1:密码破译

      温温手下的情报部门截获了一封加密信息,这个信息可以用长度为n的由小写字母构成的一个字符串表示。为了破译这个重要情报,温温决定亲自出马。

           通过不懈研究,温温推测出了这封密文是怎样被构造出来的。

      首先选择一个长度大于4的“根”字符串,然后在“根”字符串之后连接上任意个长度为2或3的“后缀”字符串。构造唯一的限制条件是,不可以连续接上两个相同的“后缀”字符串

      现在温温想要知道,在这封密文的构造过程中,有可能被用作“后缀”的字符串共有多少种。多个同样的“后缀”字符串只算一种。

     

    Input

    一行,一个长度为n的仅包含小写字母的字符串

    5 ≤ n ≤ 100 for 40%

    5 ≤ n ≤ 10000 for 100%

     

    Output

    第一行一个整数k,表示有k种可能的“后缀”。

    接下来k行,按照字典序依次输出所有可能的“后缀”。

     

    Examples

    input

    abcdefghij

    output

    5

    fg

    fgh

    gh

    hij

    ij

    input

    abaca

    output

    0

     

    Note

    在第一个样例中,有五种切分方式:

    ·abcdefgh/ij

    ·abcdefg/hij

    ·abcdef/gh/ij

    ·abcde/fgh/ij

    ·abcde/fg/hij

      

      这个题当时做的时候没有看到不准重复的限制条件,当时交上去代码不知道为什么没测,估计只有20分。

      一个关键的问题:如果出现了两个重复的连续后缀,靠后的那一个后缀是可以合法的,因为我们可以把前面的部分都算给“根”字符串;而前面的所有后缀都不合法。

      我们维护f[i][0/1]记录状态,分别表示从这个位置开始的长度为2、3的后缀是否可行。搞完麻烦的初始边界条件以后,转移更麻烦:既要保证这个串的下一个位置的0/1是否合法,也要看它和下一个后缀是否相同。判断合法后,我们直接把这个后缀插入一个set里:由于string定义的不等号是按字典序比较的,遍历set输出的元素实际上就是按字典序升序排列的。

    代码:

    1. #include <iostream>  
    2. #include <cstring>  
    3. #include <cstdio>  
    4. #include <cstdlib>  
    5. #include <set>  
    6. #define maxn 10010  
    7. void open_file(std::string s) {  
    8.     std:: string In = s + ".in", Out = s + ".out";  
    9.     freopen(In.c_str(), "r", stdin);  
    10.     freopen(Out.c_str(), "w", stdout);  
    11. }  
    12. using namespace std;  
    13. string s(1, 0), t;  
    14. bool f[2][maxn];   
    15. int len;  
    16. set<string> S;  
    17. void dp() {  
    18.     if (len < 7) {  
    19.         cout << 0;  
    20.         exit(0);  
    21.     }  
    22. //  f[0][len] = f[1][len] = 0;  
    23.     string a(s.end() - 2, s.end());  
    24.     f[0][len - 1] = 1;  
    25.     S.insert(a);  
    26.     if (len >= 8) {  
    27.         f[1][len - 2] = 1;  
    28.         string b(s.end() - 3, s.end());  
    29.         S.insert(b);  
    30.     }  
    31.     for (int i = len - 3; i > 5; --i) {  
    32.         string t;  
    33.         for (int j = i; j <= i + 1; ++j)  
    34.             t.push_back(s[j]);  
    35.         if (f[0][i + 2] || f[1][i + 2])  
    36.             if (s[i] != s[i + 2] || s[i + 1] != s[i + 3])     
    37.                 f[0][i] = 1, S.insert(t);  
    38.         t.push_back(s[i + 2]);  
    39.         if ((f[0][i + 3] || f[1][i + 3])   
    40.         && (s[i] != s[i + 3] || s[i + 1] != s[i + 4] || s[i + 2] != s[i + 5]))  
    41.             f[1][i] = 1, S.insert(t);  
    42.     }  
    43. }  
    44. int main() {  
    45.     open_file("suffix");  
    46.     ios::sync_with_stdio(0);  
    47.     getline(cin, t);  
    48.     s += t;  
    49.     len = s.size() - 1;  
    50.     dp();  
    51.     int size = S.size();  
    52.     cout << size << endl;  
    53.     set<string>::iterator it;  
    54.     int i = 1;  
    55.     for (it = S.begin(); i <= size; ++i, ++it)  
    56.         cout << *it << endl;  
    57.     return 0;  
    58. }  

    ----------------------------------------------------------

    T2:先进序列

      

      萌猪大统领温温正在为他看到的所有事物评选先进。

           温温已经评选出了n个先进整数ai,现在,他决定在此基础上评选一下先进序列。

           一个整数序列x1,x2,…xk是先进的,当且仅当它满足以下三个条件

      1、这个序列是严格递增的。

      2、序列中,任意两个相邻的元素都不是互质的,即gcd(xi, xi+1) > 1对1 ≤ i < k成立。

      3、序列中的所有元素都是先进整数。

      现在温温想知道,最长的先进序列的长度是多少。

          

    Input

    第一行,一个整数n

    第二行,n个整数ai,保证ai互不相同

    1 ≤ ai ≤ 200000

    1 ≤ n ≤ 1000 for 40%

    1 ≤ n ≤ 100000 for 100%

     

    Output

    一个整数,最长先进序列的长度。

     

    Examples

    input

    5
    2 3 4 6 9

    output

    4

    input

    9
    1 2 3 5 6 7 8 9 10

    output

    4

       (老师到底和温温有什么交易啊……三天都是一只猪

      今天的题难度确实比昨天小些,但是综合性很强。这个题的解法其实很显然,可是考场上看到数学就想打暴力……

      容易发现,我们应该把原序列进行升序排序后再按gcd的条件转移,这样就可以愉快地扫一遍来dp了。f[i]表示以位置i结尾的最长先进序列的长度,每找到一个位置,把这个数质因数分解。考虑之前含有质因子k的数的位置,开一个数组d[k]表示满足含k的位置的最大的f值。那么我们枚位置i的每一个素因子,拿之前记录的d数组+1来转移,然后再扫一遍,更新d数组的值。

      改的时候遇到一个坑点:sqrt函数不可以每次调用,否则复杂度会从O(nlog(sqrt(maxval)))退化到O(n^2)左右。

      既然写到这个题,就来重温一遍线性筛的写法。

      线性筛做到线性的关键,就是只用每个合数的最小质因子来筛掉它。

    代码:

    1. void euler() {  
    2.     for (int i = 2; i <= maxv; ++i) {  
    3.         if (!vis[i]) prime[++prime[0]] = i;  
    4.         for (int j = 1; j <= prime[0] && prime[j] * i <= maxv; ++j) {  
    5.             vis[i * prime[j]] = 1;  
    6.             if (i % prime[j] == 0)  
    7.                 break;  
    8.         }  
    9.     }  
    10. }  

      线性筛的正确性由以下两个重要性质来保证。

      1、素数k被选中以后,只会被乘上k、k+1,k+2……来筛掉它的倍数,之前的素数不会再被乘上。

      2、当我们选中的最小质数是i的因数时,以后的prime再乘上i,就一定能被prime[j]乘以某个数在以后筛掉,因此要把当前循环break掉。

    T2代码:

    1. #include <cstdio>  
    2. #include <iostream>  
    3. #include <cstring>  
    4. #include <cmath>   
    5. #include <algorithm>  
    6. #define maxn 100010  
    7. void open_file(std::string s) {  
    8.     std:: string In = s + ".in", Out = s + ".out";  
    9.     freopen(In.c_str(), "r", stdin);  
    10.     freopen(Out.c_str(), "w", stdout);  
    11. }  
    12. const int maxv = 200010;  
    13. using namespace std;  
    14. int prime[maxv], a[maxn], n;  
    15. bool vis[maxv];  
    16. void euler() {  
    17.     for (int i = 2; i <= maxv; ++i) {  
    18.         if (!vis[i]) prime[++prime[0]] = i;  
    19.         for (int j = 1; j <= prime[0] && prime[j] * i <= maxv; ++j) {  
    20.             vis[i * prime[j]] = 1;  
    21.             if (i % prime[j] == 0)  
    22.                 break;  
    23.         }  
    24.     }  
    25. }  
    26. int f[maxn], d[maxv];  
    27. void dp() {  
    28.     for (register int i = 1; i <= n; ++i) {  
    29.         f[i] = 1;  
    30.         int k = sqrt(a[i]);  
    31.         for (register int j = 1; prime[j] <= k; ++j)   
    32.             if (a[i] % prime[j] == 0)  
    33.                 f[i] = max(f[i], d[prime[j]] + 1),  
    34.                 f[i] = max(f[i], d[a[i] / prime[j]] + 1);  
    35.         if (!vis[a[i]]) {  
    36.             f[i] = max(f[i], d[a[i]] + 1);  
    37.             d[a[i]] = max(d[a[i]], f[i]);  
    38.         }  
    39.         for (register int j = 1; prime[j] <= k; ++j)   
    40.             if (a[i] % prime[j] == 0)   
    41.                 d[prime[j]] = max(d[prime[j]], f[i]),  
    42.                 d[a[i] / prime[j]] = max(d[a[i] / prime[j]], f[i]);  
    43.     }  
    44.     return;  
    45. }  
    46. int main() {  
    47.     open_file("sequence");  
    48.     ios::sync_with_stdio(0);  
    49.     cin >> n;  
    50.     for (int i = 1; i <= n; ++i)   
    51.         cin >> a[i];  
    52.     euler();  
    53.     sort(a + 1, a + n + 1);  
    54.     dp();  
    55.     int ans = 0;  
    56.     for (int i = 1; i <= n; ++i)  
    57.         ans = max(ans, f[i]);  
    58.     cout << ans;  
    59.     return 0;  
    60. }  
  • 相关阅读:
    linux学习17 运维核心技能-Linux系统下用户权限管理
    linux学习16 Linux用户和组管理命令演练和实战应用
    linux学习15 Linux系统用户和组全面讲解
    linux学习14 Linux运维高级系统应用-glob通配及IO重定向
    linux学习13 Linux运维常用文件管理命令及系统变量基础
    linux学习12 bash的常见特性及文本查看命令实战
    linux学习11 Linux基础命令及命令历史
    linux学习10 Linux目录结构和根文件系统全面讲解
    【Hadoop离线基础总结】Hive调优手段
    【Hadoop离线基础总结】Hive的基本操作
  • 原文地址:https://www.cnblogs.com/TY02/p/11210135.html
Copyright © 2020-2023  润新知