hdu-2243
n個模式串,求滿足長度小於等於k且至少包含其中一種模式串的串數(只包含
小寫字母)
k<2^31-1
多模式串的計數問題,我們用AC自動機來解決
那麼問題來了
1)怎麼求至少包含一種的串數?
正難則反,我們用所有串數減去一種都不含的就是至少包含一種的方案
2)所有長度小於等於k的串數怎麼求?即26+26^2+26 ^3+……26 ^k
首先想到的是利用 等比數列前n項和 a1*(1-q^n)/(1-q);但本題由於是對2 ^64取模,所以我們無法解決逆元的問題,那怎麼辦呢
!通過枚舉k發現 我們要求的F(k)=26F(k-1)+26;!!!
所以可以構建矩陣進行 矩陣快速冪加速
F.a[0][0]=26;F.a[0][1]=26;
F.a[1][0]=0;F.a[1][1]=0;
fd.a[0][0]=26;fd.a[0][1]=0;
fd.a[1][0]=1;fd.a[1][1]=1;
F=F*qp(fd,k-1);
3)一種都不含的串怎麼求?
首先對n個模式串建立AC自動機,並建立有向圖A[i][j]代表 I 節點到 J 節點一步能走到的方法數其中 所有模式串的結尾節點以及包含所有模式串的節點 都爲不可到達點,即所在行和列權值都爲0。那麼長度爲K的串數即爲從根節點(0節點)在圖中走K部的方法數(即接受K個字母,每接受1個字母相當於在有向圖中走一次)
又由引理:在有向圖中走K步,相當於這個有向圖的鄰接矩陣自乘K次
之後我們便可以直接對該矩陣進行快速冪,然後在對第0行求和即可
但是
此題要求所有長度小於等於K的串數
即我們要分別求出所有I=1到K 的和,這裏我們同樣可以通過構建矩陣來解決
MAT1 C1,C2;
for(int i=0;i<=tot+1;i++) for(int j=0;j<=tot+1;j++)
C1.a[i][j]=C2.a[i][j]=0;
for(int i=0;i<=tot+1;i++) C2.a[i][tot+1]=1;
for(int i=0;i<=tot;i++){
if(is[i]) continue;
for(int j=0;j<26;j++){
if(is[trie[i][j]]) continue;
C1.a[i][trie[i][j]]++;
C2.a[i][trie[i][j]]++;
}
}
C1=C1*qp(C2,k-1);
具體請看代碼
#include<bits/stdc++.h>
#define warn printf("!!!***!\n")
using namespace std;
typedef unsigned long long LL;
typedef pair<int,int>pii;
typedef pair<LL,LL>pll;
const int maxn=1e6+6;
const int mod=1e9+7;
int fail[maxn],trie[maxn][26],is[maxn],tot,n,k;
char s[maxn];
queue<int>q;
struct AC_auto
{
void init(){
for(int i=0;i<=tot;i++){
fail[i]=is[i]=0;
for(int j=0;j<26;j++){
trie[i][j]=0;
}
}
tot=0;
while(!q.empty()) q.pop();
}
void add(char *s){
int len=strlen(s),p=0;
for(int i=0;i<len;i++){
int c=s[i]-'a';
if(!trie[p][c]) trie[p][c]=++tot;
p=trie[p][c];
}
is[p]=1;
}
void build_f(){
for(int i=0;i<26;i++) if(trie[0][i])
q.push(trie[0][i]);
while(!q.empty()){
int f=q.front();q.pop();
is[f]|=is[fail[f]];
for(int i=0;i<26;i++){
if(trie[f][i]){
fail[trie[f][i]]=trie[fail[f]][i];
q.push(trie[f][i]);
}
else trie[f][i]=trie[fail[f]][i];
}
}
}
}ac;
struct MAT1
{
LL a[55][55];
MAT1 operator *(const MAT1 &k2) const {
MAT1 p;
for(int i=0;i<=tot+1;i++){
for(int j=0;j<=tot+1;j++){
p.a[i][j]=0;
for(int k=0;k<=tot+1;k++){
p.a[i][j]+=a[i][k]*k2.a[k][j];
}
}
}
return p;
}
};
struct MAT2
{
LL a[2][2];
MAT2 operator *(const MAT2 &k2) const {
MAT2 p;
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
p.a[i][j]=0;
for(int k=0;k<2;k++){
p.a[i][j]+=a[i][k]*k2.a[k][j];
}
}
}
return p;
}
};
MAT1 qp(MAT1 x,LL y)
{
MAT1 ans;
for(int i=0;i<=tot+1;i++) for(int j=0;j<=tot+1;j++)
ans.a[i][j]=(i==j?1:0);
while(y){
if(y&1) ans=ans*x;
x=x*x;
y>>=1;
}
return ans;
}
MAT2 qp(MAT2 x,LL y)
{
MAT2 ans;
for(int i=0;i<2;i++) for(int j=0;j<2;j++)
ans.a[i][j]=(i==j?1:0);
while(y){
if(y&1)ans=ans*x;
x=x*x;
y>>=1;
}
return ans;
}
int main()
{
while(~scanf("%d %d",&n,&k)){
ac.init();
for(int i=1;i<=n;i++){
scanf("%s",s);
ac.add(s);
}
ac.build_f();
MAT2 F,fd;
F.a[0][0]=26;F.a[0][1]=26;
F.a[1][0]=0;F.a[1][1]=0;
fd.a[0][0]=26;fd.a[0][1]=0;
fd.a[1][0]=1;fd.a[1][1]=1;
//cout<<n<<','<<k<<endl;
F=F*qp(fd,k-1);
//printf("%llu\n",F.a[0][0]);
MAT1 C1,C2;
for(int i=0;i<=tot+1;i++) for(int j=0;j<=tot+1;j++)
C1.a[i][j]=C2.a[i][j]=0;
for(int i=0;i<=tot+1;i++) C2.a[i][tot+1]=1;
for(int i=0;i<=tot;i++){
if(is[i]) continue;
for(int j=0;j<26;j++){
if(is[trie[i][j]]) continue;
C1.a[i][trie[i][j]]++;
C2.a[i][trie[i][j]]++;
}
}
C1=C1*qp(C2,k-1);
for(int i=0;i<=tot+1;i++) F.a[0][0]-=C1.a[0][i];
printf("%llu\n",F.a[0][0]);
}
}