题解 LGP7888【「MCOI-06」Distinct Subsequences】

problem

给定一个由小写字符构成的字符串 \(S\)

令一个字符串的价值为该串的本质不同非空子序列个数,其中子序列可以为整体。

\(S\) 所有子序列的价值和。答案对 \(10^9+7\) 取模。

对于 \(100\%\) 的数据,\(1\le |S|\le 10^6\)

solution

考虑如何求出一个字符串的价值?

\(f_i\) 表示以字符 \(i\) 为结尾有多少个本质不同的非空子序列。

增量法,每次加入字符 \(r\),将 \(f_r\) 修改为 \(1+\sum_{i}f_i\),表示将之前所有以 \(r\) 为结尾的子序列都接上 \(r\),最后补上单独的 \(r\)

我们可以将这样的过程表示为矩阵:(假设只有 \(3\) 个字符,加入字符 \(1\)

\[\begin{bmatrix} f_0 &f_1 &f_2 &1 \end{bmatrix}\times \begin{bmatrix} 1 &1 &0 &0\\ 0 &1 &0 &0\\ 0 &1 &1 &0\\ 0 &1 &0 &1 \end{bmatrix}= \begin{bmatrix} f'_0 &f'_1 &f'_2 &1 \end{bmatrix}. \]

回到原问题,要求所有子序列的价值之和。我们考虑用二项式展开的集合意义:形如

\[(a_1+1)(a_2+1)(a_3+1)=1+a_3+a_2+a_2a_3+a_1+a_1a_3+a_1a_2+a_1a_2a_3. \]

这样的东西可以求出所有子集的积的和。我们将 \(a_i\) 换成矩阵,\(1\) 换成单位矩阵,那么这个东西可以通过左式去算:

\[\begin{bmatrix}0 &0 &1\end{bmatrix} \left( \begin{bmatrix}1 &0 &0\\0 &1 &0\\0 &0 &1\\\end{bmatrix}+ \begin{bmatrix}1 &0 &0\\1 &1 &0\\1 &0 &1\\\end{bmatrix} \right) \left( \begin{bmatrix}1 &0 &0\\0 &1 &0\\0 &0 &1\\\end{bmatrix}+ \begin{bmatrix}1 &1 &0\\0 &1 &0\\0 &1 &1\\\end{bmatrix} \right). \]

结果的和就是样例一的答案。

暴力是 \(O(n|\Sigma|^2)\) 的,但是我们发现这个矩阵只有 \(O(n)\) 个有效值,因此复杂度降为 \(O(n|\Sigma|)\),可以通过。

solution 2

强制数这样的子序列,满足 \(i,j\) 之间没有 \(S_j\),令 \(f_i\),则 \(f_i=\sum_j f_j\times 2^{i-j-cnt_{S_j}(i,j)}\)

code

点击查看代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <numeric>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
const int P=1e9+7;
void red(LL&x){x=(x%P+P)%P;}
int n;
char buf[1000010];
vector<LL> append(vector<LL> f,int r){
	LL sum=accumulate(f.begin(),f.end(),0ll)%P;
	vector<LL> g(27);
	for(int i=0;i<27;i++){
		red(g[i]=f[i]);
		if(i==r) red(g[i]=sum);
		red(g[i]+=f[i]);
	}
	return g;
}
void print(vector<LL>&f){
//	for(LL x:f) debug("%lld ",x);
//	puts("");
}
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	scanf("%s",buf+1),n=strlen(buf+1);
	vector<LL> f(27,0); f[26]=1;
	for(int i=1;i<=n;i++) f=append(f,buf[i]-'a'),print(f);
	printf("%lld\n",accumulate(f.begin(),prev(f.end()),0ll)%P);
	return 0;
}

posted @ 2022-11-23 15:56  caijianhong  阅读(21)  评论(0编辑  收藏  举报