Lightoj-1268 Unlucky Strings(kmp&&矩阵快速幂)

该博客介绍了如何使用KMP算法和矩阵快速幂解决Lightoj-1268问题,涉及从给定起点出发,经过特定步数到达目标点的路径计数。通过将字符串转化为节点并构建转移矩阵,结合KMP的next数组建立初始矩阵,最终通过矩阵快速幂计算出答案。强调了矩阵运算和KMP理解在解题中的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

http://lightoj.com/volume_showproblem.php?problem=1268

题意:

给出a个字符,求用这a个字符组成的长度为n的字符串中不包含子串s的个数。


思路:

首先知道矩阵可以解决这样一类经典问题:

给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值

把给定的图转为邻接矩阵,即A(i,j)=1当且仅当存在一条边i->j。令C=A*A,那么C(i,j)=ΣA(i,k)*A(k,j),实际上就等于从点i到点j恰好经过2条边的路径数(枚举k为中转点)。类似地,C*A的第i行第j列就表示从i到j经过3条边的路径数。同理,如果要求经过k步的路径数,我们只需要二分求出A^k即可。

那么我们就可以把这个字符串中的点转换成lens个节点,0节点表示起始节点,lens节点表示匹配到字符串最后一个节点。mat[i][j]就是从i节点到j节点,即已经匹配到i字符下面要匹配j字符可能的方法数。当匹配到lens就是包含这个字符串,那么只需要建立mat[lens][lens]的矩阵,每次乘积都是增加一个字符之后从i字符匹配到j字符的方法数。在n次乘积之后从0节点到其他每个节点的方法数之和也就是从起始到最后匹配到某个非最终节点的路径数之和。也就是答案数了。

然后对于起始矩阵的建立,可以借用kmp的next数组完成每个字符节点的转移,也就是判断当前字符匹配位置之后加某个字符能够最大匹配到的位置。

很好的矩阵理解题,还有对kmp的next数组的理解,矩阵的知识还是要多看看的,数学还是很重要。

代码:

//kopyh
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define N 112
using namespace std;
int n,m,sum,res,flag;
void getNext(char *pre, int len, int *next)
{
    int i = 0,j = -1;
    next[0] = -1;
    while(i < len)
    {
        if(j == -1 || pre[i] == pre[j])
        {
            i++,j++;
            if(pre[i]!=pre[j]) next[i] = j;
            else next[i] = next [j];
        }
        else   j = next[j];
    }
}
struct Matrix
{
    unsigned ma[N][N];
    int n,m;
};
Matrix matMul(Matrix m1, Matrix m2)
{
    Matrix ans;
    memset(ans.ma,0,sizeof(ans.ma));
    for(int i=0;i<m1.n;i++)
        for(int j=0;j<m2.m;j++)
            for(int k=0;k<m1.m;k++)
                ans.ma[i][j]=(ans.ma[i][j]+m1.ma[i][k]*m2.ma[k][j]);
    ans.n=m1.n; ans.m=m2.m;
    return ans;
}
Matrix matPow(Matrix m1, int k)
{
    Matrix ans;
    for(int i=0;i<m1.n;i++)
        for(int j=0;j<m1.m;j++)
            ans.ma[i][j] = (i==j);
    ans.n=ans.m=m1.n;
    while(k)
    {
        if(k&1)ans = matMul(ans,m1);
        m1 = matMul(m1,m1);
        k>>=1;
    }
    return ans;
}
char s[N],ss[N];
int a[N];
int main()
{
    int i,j,k,cas,T,t,x,y,z;
    scanf("%d",&T);
    cas=0;
    while(T--)
    {
        scanf("%d%s%s",&n,&s,&ss);
        x=strlen(s);
        y=strlen(ss);
        getNext(ss,y,a);
        Matrix ans;
        memset(ans.ma,0,sizeof(ans.ma));
        ans.n=ans.m=y;
        for(i=0;i<y;i++)
            for(j=0;j<x;j++)
            {
                t = i;
                while(t && ss[t]!=s[j])t = a[t];
                if(ss[t] == s[j])t++;
                ans.ma[i][t]++;
            }
        ans = matPow(ans,n);
        unsigned res=0;
        for(i=0;i<y;i++)res+=ans.ma[0][i];
        printf("Case %d: %u\n",++cas,res);
    }
    return 0;
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值