• 题解-gym102759G[*medium]


    题面

    gym102759G LCS 8

    给定一字符串 (S)。求有多少不同的长度为 (T),使得 (|S| = |T|),且 (S)(T) 的最长公共子序列长度大于等于 (n-k)。所有字符都为大写字母。

    数据范围:(1 le |S| le 5 imes 10^4, 0 le k le 3)

    题解

    (下文令 (n = |S|)

    我们是如何 dp(lcs) 的?

    记录 (a_{i, j}) 表示 (S) 的前 (i) 个字符 和 (T) 的前 (j) 个字符的 (lcs) 。有 (a_{i, j} = max(a_{i - 1, j}, a_{i, j - 1}, a_{i - 1, j - 1} + [a_i = b_j]))

    如果我们对于每一个 (T) 中的位置 (j),记录对于所有 (i)(lcs(i, j)) 是多少。但是这样状态数过多,对于每一个位置 (j) 都有 (n^n) 个。

    用一个常见的差分套路,记录 (lcs(i, j) - lcs(i - 1, j)),这个值只有 (0)(1),这样状态数减少到 (2^n)

    我们发现 (lcs(i, j) le min(i, j)),而由这个点对 ((i, j)) 更新的 (lcs(S, T)) 不超过 (lcs(i, j) + min(n - i, n - j))

    如果 (min(i, j) + min(n - i, n - j) ge n - k)((i, j)) 这个点对才可能对答案有贡献。化简一下得到 (max(i, j) - min(i, j) le k) 。所以我们记录满足 (|i - j| le k) 的状态即可。

    再记录一下 (lcs(i, i)),状态数变成了 ((k + 1) 2^{2k})

    转移很简单,枚举 (T_j) 是哪一个字符,然后更新最长公共子序列的状态就可以了。

    时间复杂度垃圾,是 (Theta(26nk^22^{2k}))。空间复杂度 (Theta(nk 2^{2k})) ,可以滚动数组优化到 (Theta(k 2^{2k}))

    被卡常了,加了个类似记忆化的东西就过了。

    CF578D 是这题的弱化版,把 (m) 改为 (1),字符集改一改就过了。

    代码

    gym102759G:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N = 1e5 + 7;
    const int mod = 1e9 + 7;
    int n, m, now[N], nxt[N], ans;
    int dp[N][1 << 7][5]; 
    bool mp[256];
    char s[N]; 
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(0), cout.tie(0);
    	cin >> (s + 1) >> m, n = strlen(s + 1);
    	if(m == 0) return cout << "1
    ", 0;
    	dp[0][0][0] = 1;
    	for(int i = 1; i <= n; i++) {
    		memset(mp, 0, sizeof(mp));
    		for(int j = max(i - m, 1); j <= min(i + m, n); j++) mp[s[j]] = 1;
    		
    		for(int j = 0; j < (1 << (m << 1)); j++) {
    			for(int k = 0; k <= m; k++) if(dp[i - 1][j][k]) { 
    				now[i - 1] = i - 1 - k;
    				for(int t = 0; t < m; t++) now[i + t] = now[i + t - 1] + (j >> (t + m) & 1);
    				for(int t = 1; t <= m; t++) if(i - 1 - t > 0) now[i - 1 - t] = now[i - t] - (j >> (m - t) & 1);
    				int to1 = -1, to2;
    				for(char t_i = 'A'; t_i <= 'Z'; t_i++) {
    					if(!mp[t_i] && ~to1) {
    						(dp[i][to1][to2] += dp[i - 1][j][k]) %= mod;
    						continue;
    					}
    					int msk = 0;
    					for(int wz = max(i - m, 1); wz <= i + m; wz++) 
    						nxt[wz] = max(max(now[wz], now[wz - 1] + (s[wz] == t_i)), nxt[wz - 1]);
    					for(int wz = max(i - m, 0); wz <= i + m - 1; wz++) if(nxt[wz] ^ nxt[wz + 1]) msk |= (1 << (wz - i + m));
    					(dp[i][msk][i - nxt[i]] += dp[i - 1][j][k]) %= mod;
    					if(!mp[t_i]) to1 = msk, to2 = i - nxt[i];
    				}
    			}
    		}
    	}
    	for(int i = 0; i < (1 << (m << 1)); i++) for(int j = 0; j <= m; j++) (ans += dp[n][i][j]) %= mod;
    	cout << ans << "
    ";
    	return 0;
    } 
    

    CF578D:

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int N = 1e5 + 7;
    int n, S, now[N], nxt[N];
    ll dp[N][4][3], ans; 
    bool mp[26];
    char s[N]; 
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(0), cout.tie(0);
    	cin >> n >> S, cin >> (s + 1);
    	dp[0][0][0] = 1;
    	for(int i = 1; i <= n; i++) {
    		memset(mp, 0, sizeof(mp));
    		for(int j = max(i - 1, 1); j <= min(i + 1, n); j++) mp[s[j] - 'a'] = 1;
    		for(int j = 0; j < 4; j++) {
    			for(int k = 0; k <= 1; k++) if(dp[i - 1][j][k]) { 
    				now[i - 1] = i - 1 - k, now[i] = now[i - 1] + (j >> 1 & 1);
    				if(i - 2 > 0) now[i - 2] = now[i - 1] - (j & 1); 
    				int to1 = -1, to2;
    				for(char t_i = 'a'; t_i <= 'a' + S - 1; t_i++) {
    					if(!mp[t_i - 'a'] && ~to1) {
    						dp[i][to1][to2] += dp[i - 1][j][k];
    						continue;
    					}
    					int msk = 0;
    					for(int wz = max(i - 1, 1); wz <= i + 1; wz++) nxt[wz] = max(max(now[wz], now[wz - 1] + (s[wz] == t_i)), nxt[wz - 1]);
    					for(int wz = max(i - 1, 0); wz <= i; wz++) if(nxt[wz] ^ nxt[wz + 1]) msk |= (1 << (wz - i + 1));
    					dp[i][msk][i - nxt[i]] += dp[i - 1][j][k];
    					if(!mp[t_i - 'a']) to1 = msk, to2 = i - nxt[i];
    				}
    			}
    		}
    	}
    	for(int i = 0; i < 4; i++) ans += dp[n][i][1];
    	cout << ans << "
    ";
    	return 0;
    } 
    

    祝大家学习愉快!

  • 相关阅读:
    Python 基础(二)
    Python 入门
    DNS
    PXE自动化安装CentOS7和CentOS6
    AIDE及sudo应用
    SSH应用
    KickStart自动化安装Linux
    初见鸟哥
    数组ARRAY
    SSH用法
  • 原文地址:https://www.cnblogs.com/zkyJuruo/p/14401116.html
Copyright © 2020-2023  润新知