hdu-5318 The Goddess Of The Moon(2015 Multi-University Training Contest 3)

本文介绍了一种使用矩阵快速幂解决特定字符串匹配问题的高效方法,包括了矩阵快速幂模板、字符串去重技巧、字符串与char[]互换及数字转换成char[]的实现,并详细阐述了解决题目的思路与步骤。通过实例展示了如何利用矩阵快速幂解决复杂字符串匹配问题。

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


首先放一个矩阵快速幂的模板。

#include<vector>

typedef vector<long long> vec;
typedef vector<vec> mat;
typedef long long ll;
const int M = 1000000007;

mat mul(mat &A,mat &B)
{
    mat C(A.size(), vec(B[0].size()));
    for (int i = 0; i < A.size(); i++)
        for (int k = 0; k < B.size(); k++)
            for (int j = 0; j < B[0].size(); j++)
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) %M;
    return C;
}

mat pow(mat A, ll n){
    mat B(A.size(), vec(A.size()));
    for (int i = 0; i < A.size(); i++){
        B[i][i] = 1;
    }
    while (n > 0){
        if (n & 1) B = mul(B,A);
        A = mul(A,A);
        n >>= 1;
    }
    return B;
}
void solve(int n,int m)
{
    mat a(n,vec(n));//n是行数,n也是列数
    for (int i = 0;i < n; i++){
        for (int j = 0; j < n; j++){
            if (check(luf[i],luf[j])) a[i][j] = 1;
            else a[i][j] = 0;
        }
    }
    a = pow(a,m);
    printf("%I64d\n",a[0][0]);
}




set可以用于去重。

以下包括了set的插入,set的查找,set的遍历。

<span style="white-space:pre">	</span>#include<set>
        set<int> st;
        st.clear();
        for (int i = 0; i < n; i++){
            int tmp;
            scanf("%d",&tmp);
            if (st.find(tmp) == st.end()){
        //上面这句判断去掉也没关系。亲测可行。但是顺便写一下set查找用法嘛~map查找也是一样
                st.insert(tmp);
            }
        }

        //遍历st集合
        set<int>::reverse_iterator rit;
        for(rit=st.rbegin();rit!=st.rend();rit++){
            printf("%d   ",*rit);
        }
        printf("set = %d\n",realn);



接下来是string和char[]的互相转换、把数字转换成char[]string 子串的比较。

    //printf打印string类的方法。不推荐。
    string s = "aaa";
    printf("%s", s.c_str());

    //char[] 转成 string
    char ch[10] = "qwe";
    string pp = ch;

    //string 转成 char []
    string pp = "asdf";
    char p[8];
    int i;
    for( i=0;i<pp.length();i++)
        p[i] = pp[i];
    p[i] = '\0';
    printf("%s",p);

    //把set中的string复制到char中的方法
    char ch[SIZE_N][SIZE_N];
    set<string> st;
    int tn = 0;
    for(auto &it:st) strcpy(ch[++tn], it.c_str());
    n = tn;

    //把数字转换成字符串的方法
    char s1[20],s2[20];
    sprintf(s1,"%d",a);
    sprintf(s2,"%d",b);
    int len1=strlen(s1),len2=strlen(s2);
    
    //字符串的比较。
    //方法一:
    if(string(s1+i,s1+len1)==string(s2,s2+j+1))//相当于[s1[i],s1[len1-1] ] 和 [s2[0],s2[j] ] 这两个比较
    //方法2:
    string a,b;
    string ta,tb;
    for(i=0; i<la; i++) {
        ta=a.substr(i);
        for(j=0; j<=lb; j++) {
            tb=b.substr(0,j);
            if(ta==tb && (int)ta.length()>1) return 1;
        }
    }


题意:给你很多个数字串。如果A串的后缀正好是B串的前缀(前缀大于等于2),那么A就可以和B串连在一起。问你如果想要变成一条一共m个串的链,有几种方案。


比如:

m=2

123    234

那么就有123自己接自己一种,234 自己接自己一种,123接234一种。共三种方式。


题解:

1、首先。要考虑自己可以接自己啊!!!那么就算你给你三个数字接成一个环也有炒鸡多种接法。

2、画成图。就会发现是从A点到B点,途中经过m个点有几种方案数的问题。(据说这是经典的矩阵快速幂问题,我真是孤陋寡闻)

3、列dp方程式。dp[i][j]方程中i代表选择了i个串,j代表结尾是j
dp[i][j] = 求和dp[i-1][k].并且k和j可以相连
构造a矩阵,ij相连这个地方就是1.
example:dp[i][3] = dp[i-1][1] * a[1][3] + dp[i-1][2] * a[2][3] + dp[i-1][3] * a[3][3];
构造矩阵之后每次就把数字求出来就可以了
用矩阵快速幂。构造矩阵a之后求m - 1  次幂出来即可。

4、然后具体这个矩阵a是怎么推导出来的。

现在想象有一个3*3的矩阵A,A[i][j] 的数字为1表示i到j有路可以走。那么A^2就表示i走两步到达j有几种方法,A^3就表示i走三步到j有几种方法。A^(m-1)中的每一个A[i][j]表示i到j经过m-2个点,也就是包括首尾经过一共m个点。整个A^(m-1)的所有的元素求和求一遍,就是最后的答案啦~


注意点:

1、矩阵快速幂的时候记得把这个矩阵弄成long long,否则算的时候会溢出的~

2、这道题的一个坑点就是输入的串可能会重复。以下是预防重复的三种方法。

清除重复串和数字转字符串的方法
        //清除重复串的方法1:用数组和unique
        int use[SIZE_N];
        for(i=0;i<N;i++)
            scanf("%d",&use[i]);
        sort(use,use+N);
        N=unique(use,use+N)-use//此时的use是已经无重复串的一个数组啦

        //清除重复串的方法2:用set和另一个乱七八糟的东西
        set <string> st;
        for(int i = 1;i <= n; i++) {
            scanf("%s", ch[i]);
            st.insert(ch[i]);
        }
        int tn = 0;
        for(auto &it:st) strcpy(ch[++tn], it.c_str());
        n = tn;

        //清除重复串的方法3:用map
        map<string,int> mp;
        for(i=0; i<n; i++) {
            cin>>ss;
            if(mp.find(ss)==mp.end()) {
                mp[ss]=k++;
                str[cnt++]=ss;
            }
        }
        N=cnt;





——————————我是预防傻逼博客它贴了代码后再按回车另一起一行就不能写文字而变成代码的分割线——————————————


那么我用了哪种方法呢?那就是三种都没用~哈哈哈哈就是这么可爱哈哈哈哈

以下是本题代码。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<vector>
#define SIZE_N 330
using namespace std;

typedef vector<long long> vec;
typedef vector<vec> mat;
typedef long long ll;

int luf[SIZE_N];
int realn;

int a[SIZE_N][SIZE_N];

bool check(int a,int b)  //ok
{
    char s1[20],s2[20];
    sprintf(s1,"%d",a);
    sprintf(s2,"%d",b);
    int len1=strlen(s1),len2=strlen(s2);
    for(int i=len1-1,j=0;i>=0 && j<len2;i--,j++)
        if(len1-i>=2 && string(s1+i,s1+len1)==string(s2,s2+j+1))
            return true;
    return false;
}

int checkori(int a,int b)
{
    //printf("\na = %d b = %d\n",a,b);
    if (a <10 || b < 10) return 0;
        char s1[20],s2[20];
        sprintf(s1,"%d",a);
        sprintf(s2,"%d",b);
        int len1=strlen(s1),len2=strlen(s2);//表示每个串的非终结符部分一共几位
        //printf("a = %d  b = %d   %d %d",a,b,len1,len2);
        for (int i = len1-2; i >= 0; i--){
            int j = len1- i;
            if (string(s1 + i, s1 + len1) == string(s2,s2+j)){
                //printf("a = %d b = %d\n",a,b);
                return 1;
            }
        }
        return 0;
}

const int M = 1000000007;

mat mul(mat &A,mat &B)
{
    mat C(A.size(), vec(B[0].size()));
    for (int i = 0; i < A.size(); i++)
        for (int k = 0; k < B.size(); k++)
            for (int j = 0; j < B[0].size(); j++)
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) %M;
    return C;
}

mat pow(mat A, ll n){
    mat B(A.size(), vec(A.size()));
    for (int i = 0; i < A.size(); i++){
        B[i][i] = 1;
    }
    while (n > 0){
        if (n & 1) B = mul(B,A);
        A = mul(A,A);
        n >>= 1;
    }
    return B;
}
void solve(int n,int m)
{
    mat a(n,vec(n));
    for (int i = 0;i < realn; i++){
        //printf("%d   ",luf[i]);
        for (int j = 0; j < realn; j++){
            if (check(luf[i],luf[j])) a[i][j] = 1;
            else a[i][j] = 0;
        }
    }
    //记得把a矩阵也换成 long long

    a = pow(a,m-1);
    long long res = 0;
    for (int i = 0; i < realn; i++)

        for (int j =  0; j < realn; j++)
            res = (res + a[i][j]) %M;


        //res = (res + a[0][i]) % M;
    printf("%I64d\n",res);
}


set<int> st;
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    #endif
    int T,n,m;
    scanf("%d",&T);
    while (T--){
        scanf("%d %d",&n,&m);
        realn = 0;
        for (int i = 0; i < n; i++){
            scanf("%d",&luf[i]);
        }
        sort(luf,luf+n);
        realn=unique(luf,luf+n)-luf;//此时的use是已经无重复串的一个数组啦
        solve(realn, m);
    }
    return 0;
}

————————END————————















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值