1. 程式人生 > 其它 >21.10.10模擬 字串

21.10.10模擬 字串

給定一個長度為n的字串,串中的字元保證是前k個小寫字母。你可以在字串後再新增m個字元,使得新字串所包含的不同的子序列數量儘量多。當然,前提是隻能新增前k個小寫字母。求新的長度為n+m的串最多的不同子序列數量。答案對1e9+7取模。

一直想不明白怎麼加串。。後來發現對於要加的直接貪心找每種字元加上後的最優結果。

怎麼求本質不同的子序列呢?

我們可以設last_i為字元s[i]上一次出現的位置,f_i為第i為結尾新出現的不同子序列數量

考慮新加入一個i,那麼第x位結尾
\(last[i] \le x \le i-1\) 新出現的子序列都可以加上i得到一個新的

怎麼樣證明這樣不會產生重複的子序列呢
反證法
如果如果以last[i] − 1之前的某個位置為結尾的子序列接上第i位,會產生一個新的子序列,那麼之前的子序列直接加上last[i] 也可以產生這個新的子序列。
這與這個子序列在i第一次出現矛盾

\(f[i]=\sum^{i-1}_{j=last[i]} f[j]\)

字首和優化

f[i]表示列舉到第i位所有本質不同子序列數量,考慮第i位的數字a[i]要不要加

如果a[i]沒出現過,那麼f[i]就是相當於比f[i-1]多了一倍,而且還多了a[i]這個長度為1的子序列

如果a[i]出現過,那麼就容斥減去

	rep(i, 2, len) {
		if(!last[a[i]]) f[i] = 1ll * f[i - 1] * 2 % mod + 1;
		else f[i] = 1ll * f[i - 1] * 2 % mod - f[last[a[i]] - 1];
		if(f[i]<mod) f[i]+=mod;
		if(f[i]>mod) f[i]%=mod;
		last[a[i]]=i;
	}

本題就是後面m個多了個列舉字元。


int m, k, ans = 1, n;
int f[N], last[N];
int main() {
	freopen("string.in", "r", stdin);
	freopen("string.out", "w", stdout);
	cin >> m >> k;
	char ch;
	while(cin >> ch) {
		int x = ch - 'a' + 1;
		if(x < 1 || x > 26) break;
		int tmp = ans;
		ans = (ans*2%mod - f[x] + mod) % mod;
		f[x] = tmp;
		last[x] = ++n;
	}

	rep(i, 1, m) {
		int x(0);
		rep(j, 1, k) {
			if(!x || last[x] > last[j]) x = j; //Ì°ÐÄÕÒ×îÇ°ÃæµÄ
		}
		last[x] = ++n;
		int tmp = ans;
		ans = (ans*2%mod - f[x] + mod) % mod;
		f[x] = tmp;
	}
	cout << ans << '\n';
	return 0;
}

本文來自部落格園,作者:{2519},轉載請註明原文連結:https://www.cnblogs.com/QQ2519/p/15392312.html