有M个字符串,这M个字符串不能出现在字符串中。要求字符串长度为N,问有多少种方案。
首先,对于这道题,需要知道一个数学定理。对于一张图的邻接矩阵,邻接矩阵的N次幂就是两点距离为N的路径条数。
知道了上述定理以后,这道题就可以看作,字典树从0开始,到某个节点,路径长度为N的方案个数。因为存在不能出现的字符串,结合AC自动机便可解决该题。利用AC自动机对不能出现的字符串,以及后缀子串是不能出现的字符串的节点进行标记。(因为在字典树上,如果想从0开始访问这个节点,那就必须要构成危险字符,才能访问这个节点。)在构建矩阵的时候,被标记的节点值为0。
构建矩阵完成后,利用矩阵快速幂方法进行运算。最后矩阵第一行的和即为所求。
事实上,把AC自动机的边映射到矩阵上整个过程详细分析的话还是比较难的。大概可以这样理解。例如拿“ACAGAAA”和”ACAG”进行分析。首先的话,第一个字符我们可以选择A或者GCT,这样的话便有矩阵[3,1,0,0,0,0,0,0]。如果我们要选择第二个字符的话,我们可以看一下,每个节点转移到0状态的路径矩阵[3,2,3,1,0,2,2,0]T。两个矩阵相乘,所代表的意义就是从0出发,经过某个点,再回到0。(从0出发是必要的,但是再回到0不是必要的,因此最后相乘的结果存放在第一行的第一列,第一行其他列代表不回到0的情况)这样依此类推,便可以解决问题。
当然,有一些细节还是要说明一下。比如说ACAGAAA,我们可以看到,转移到0状态的路径矩阵为[3,2,3,1,0,2,2,0]T,也就是说倒数第二个A和倒数第三个A也是有能力转移到0点的。这样的话,会不会存在不符合条件的字符串转移到了0点的情况呢?
仔细想一下的话,就会发现这种情况是不存在的。因为如果想要转移到0点,首先就要到达这个点,换句话说就是初始第一行[3,1,0,0,0,0,0,0],经过某些变换后,要能使倒数第二个数和倒数第三个数变为1以上,这样的话才有可能进行转移。
我们可以分析一下,我们可以首先打印一下6号点的转移情况(矩阵第七列),看看谁能转移到6号点,我们发现矩阵为[0,0,0,0,0,1,0,0],在这里我们发现,5号点有一条边可以转移到6号点。(也就是我们已经选择了A,再选一个A)然后,我们再打印一下5号点的矩阵,看看谁能转移到5号点,结果发现是[0,0,0,0,0,0,0,0]。我们发现,4号点和5号点有边相连。原本如果处于四号点状态,再选一个A就可以达到5号点。这时候,对四号点进行处理的作用就显现出来了。对四号点进行过滤,使得四号点不能进行任何状态转移,也不允许任何点转移到四号状态,意义就在于此。
我们可以尝试对状态矩阵进行一千次快速幂运算,结果正如我们所想的。0-3号点都不为0,4号点以后都为0。这也就意味着,0-3号点,都可以进行转移,组成字符串。
上述就是我对利用矩阵+AC自动机计算字符串组成方案的一些思考,主要就是为了验证矩阵+AC自动机算法的正确性。如有错误,还望赐教。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define UP(i,l,h) for(int i=l;i
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f3f3f3f3f
#define LL long long
#define MAXN 10000
#define EPS 1e-10
#define MOD 100000
#define N 2
using namespace std;
typedef LL MATRIX[110][110];
int ch[110][4];
char fea[15];
int len,sz;
int val[110];
int f[110];
MATRIX a,temp,ans;
int getNum(char c) {
if(c=='A') {
return 0;
}
if(c=='C') {
return 1;
}
if(c=='T') {
return 2;
}
if(c=='G') {
return 3;
}
}
void insert() {
len=strlen(fea);
int u=0;
UP(i,0,len) {
int x=getNum(fea[i]);
if(!ch[u][x]) {
ch[u][x]=sz++;
}
u=ch[u][x];
}
// cout<
val[u]=1;
}
void getFail() {
queue<int> q;
MEM(f,0);
UP(x,0,4) {
if(ch[0][x]) {
q.push(ch[0][x]);
}
}
int s=0;
W(!q.empty()) {
int r=q.front();
q.pop();
UP(x,0,4) {
int u=ch[r][x];
if(!u) {
ch[r][x]=ch[f[r]][x];
continue;
}
q.push(u);
int v=f[r];
f[u]=ch[v][x];
if(val[f[u]]==1) {
val[u]=1;
}
}
}
}
void buildMatrix() {
MEM(a,0);
UP(i,0,sz) {
UP(j,0,4) {
if(val[i]||val[ch[i][j]]) {
// cout<
continue;
}
a[i][ch[i][j]]++;
}
}
}
void matrixMulti(MATRIX a,MATRIX b) {
MEM(temp,0);
UP(i,0,sz) {
UP(j,0,sz) {
UP(k,0,sz) {
temp[i][j]=(temp[i][j]+a[i][k]*b[k][j])%MOD;
// cout<
}
}
}
memcpy(a,temp,sizeof(temp));
}
void quick(int n) {
MEM(ans,0);
UP(i,0,sz){
ans[i][i]=1;
}
W(n){
if(n&1){
matrixMulti(ans,a);
}
matrixMulti(a,a);
n>>=1;
}
}
LL getAns(){
LL sum=0;
UP(i,0,sz){
sum+=ans[0][i];
}
return sum;
}
int main() {
int m,n;
W(~scanf("%d%d",&m,&n)) {
MEM(val,0);
MEM(ch,0);
sz=1;
UP(i,0,m) {
scanf("%s",fea);
insert();
}
getFail();
buildMatrix();
quick(n);
printf("%I64d\n",getAns()%MOD);
}
}
/*
2 20000000000
ACG
C
*/