题意
给一个长度为n的字符串,有q次询问,每次询问[l,r]构成的字符串有多少个本质不同的子序列。
n,q≤105
n
,
q
≤
10
5
,字符集大小为9.
分析
先考虑如何暴力求区间内本质不同的子序列数。
我们可以每个位置向他往后第一次出现的那些字符连边,然后建一个虚点往区间从左往右第一次出现的字符连边,那么显然从虚点开始走的路径数就是答案。
设
fi,j
f
i
,
j
表示以区间内的第一个字符
i
i
为开头,在上述DAG上走,走到位置的方案。
考虑当
j
j
往左移一位后dp数组会发生什么变化。
不难发现,对于其余的
j
j
,有
我们设
ansi=∑rj=lfi,j
a
n
s
i
=
∑
j
=
l
r
f
i
,
j
,那么每次
j
j
左移一位可以看成
最后
∑8i=0ansi
∑
i
=
0
8
a
n
s
i
即为答案。
我们可以把每次
ans
a
n
s
的变化写成一个10*10的矩阵,那么我们要求的就是
al∗al+1∗...∗ar
a
l
∗
a
l
+
1
∗
.
.
.
∗
a
r
。
这样的话就只用预处理出矩阵的前缀和还有逆矩阵的前缀和,就可以直接得到答案了。
注意在求答案的时候可以用向量乘矩阵,还有在矩阵求逆的时候,会发现这个矩阵有特殊的性质,可以直接
O(n2)
O
(
n
2
)
求逆。
时间复杂度
O(nc3+qc2)
O
(
n
c
3
+
q
c
2
)
,其中
c
c
<script type="math/tex" id="MathJax-Element-2307">c</script>是字符集大小。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
typedef long long LL;
const int N=100005;
const int MOD=1000000007;
int n,ans[10],tmp[10];
char str[N];
struct Matrix
{
int a[10][10];
Matrix operator * (const Matrix &b) const
{
Matrix c;
memset(c.a,0,sizeof(c.a));
for (int i=0;i<=9;i++)
for (int k=0;k<=9;k++)
for (int j=0;j<=9;j++)
(c.a[i][j]+=(LL)a[i][k]*b.a[k][j]%MOD)%=MOD;
return c;
}
Matrix get_inv(int x)
{
Matrix b;
memset(b.a,0,sizeof(b.a));
for (int i=0;i<=9;i++) b.a[i][i]=1;
for (int i=0;i<=9;i++)
if (i!=x) b.a[i][x]=MOD-1;
return b;
}
}ma[10],inv[10],a[N],b[N];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void pre()
{
for (int i=0;i<9;i++)
{
for (int j=0;j<=9;j++) ma[i].a[j][j]=1;
for (int j=0;j<=9;j++) ma[i].a[j][i]=1;
inv[i]=ma[i].get_inv(i);
}
for (int i=0;i<=9;i++) a[0].a[i][i]=b[0].a[i][i]=1;
for (int i=1;i<=n;i++)
{
a[i]=a[i-1]*ma[str[i]-'a'];
b[i]=inv[str[i]-'a']*b[i-1];
}
}
int main()
{
scanf("%s",str+1);
n=strlen(str+1);
pre();
int q=read();
while (q--)
{
int l=read(),r=read(),sum=0;
for (int i=0;i<=9;i++) ans[i]=0;
ans[9]=1;
for (int i=0;i<=9;i++)
{
int s=0;
for (int j=0;j<=9;j++)
(s+=(LL)ans[j]*b[l-1].a[j][i]%MOD)%=MOD;
tmp[i]=s;
}
for (int i=0;i<9;i++)
{
int s=0;
for (int j=0;j<=9;j++)
(s+=(LL)tmp[j]*a[r].a[j][i]%MOD)%=MOD;
(sum+=s)%=MOD;
}
printf("%d\n",sum);
}
return 0;
}