今天浅试了一下vscode的typora插件和cnblog插件,这篇文章是typora插件编写,cnblog插件发布的

Problem

题目描述

给你两个字符串 \(S\)\(T\) ,有 \(q\) 次询问,每次询问给定一个 \(k\)

对于每次询问,你需要输出至少多少个 \(S\) 拼起来才能不为 \(k\)\(T\) 拼起来的子序列

注意是子序列!!! 吐槽:考试的时候因为看成子串了所以浪费了半天。

输入格式

第一行一个整数 \(q\)

第二行 \(S\)

第三行 \(T\)

接下来 \(q\) 行,每行一个正整数 \(k\)

输出格式

\(q\) 行,对于每次询问输出题目要求输出的值。

数据范围

\[对于 100\% 的数据,1\le n,m\le 5000,1\le q\le 3\times 10^5,1\le k\le 10^{14} \]

Solution

考虑从 \(S\) 串的每个位置开始,匹配一遍 \(T\) 串做出的贡献。例如对以下样例的两个串匹配:

3
abaab
abaabacaba
1
3
4

对于 \(S\) 串从第 \(1\) 个位置开始匹配,匹配一遍 \(T\) 串做出的贡献是一个子序列+\(S\) 的前三个字符。

这样可以 \(O(nm)\) 的预处理出来从 \(S\) 每个位置开始匹配一遍 \(T\) 做出的贡献。

然后完美可以把从每个位置开始匹配的状态看作一个点,由于从 \(1\) 开始匹配后做出的贡献是一个子序列+\(S\) 的前三个字符,故下一次匹配 \(T\) 的时候是从第 \(4\) 号位置开始匹配的,所以我们将 \(1\) 号点向 \(4\) 号点连一条单向边,同理我们可以建出来一个图。不难发现因为每一个点都有一条延申出去的单向边,故,此图存在一个环,如下图:

img

我们可以做一个类似于图上前缀和的东西,因为最初匹配是从 \(1\) 为起点开始匹配的,所以 \(1\) 号点作为根节点,然后做图上前缀和,前缀贡献。然后我们可以求出没有进入环的部分点数和环的部分点数。

对于每次询问 \(k\) 。我们可以分为两种种情况讨论

  • \(k<没有进入环的部分的点数\)
  • \(k > 进入环的部分点数\)

对于第二种情况,还要算最后环中余下点的贡献。

这么讲可能有点抽象,具体更具代码理解会更透彻。

CODE

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e3+7;
int Q;
int n,m;
string S,T;
int nxt[N],sum[N];
bool vis[N];
int pre_sum,nt_sum[N],lent,be,dep[N];
int num[N];
void dfs(int u,int dept){
    dep[u]=dept;
    num[dept]=num[dept-1]+sum[u];
    vis[u]=true;
    if(vis[nxt[u]]){
        be=dep[nxt[u]];
        lent=dept-be+1;
        pre_sum=num[be-1];nt_sum [0]=num[dept]-pre_sum;
        return;
    }
    dfs(nxt[u],dept+1);
    if(dept>=be){
        int ltt=dept-be+1;
        nt_sum[ltt]=num[dept]-pre_sum;
    }
}
signed main(){
    clock_t st,ed;
    st=clock();
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    scanf("%lld",&Q);
    cin>>S>>T;
    n=S.length();m=T.length();
    for(int i=1;i<=n;++i){
        int idt=i-1;
        for(int j=1;j<=m;++j){
            if(S[idt%n]==T[j-1]){
                idt++;
            }
        }
        idt;
        sum[i]=(idt)/n;
        nxt[i]=(idt)%n+1;
    }
    dfs(1,1);
    while(Q--){
        int k;
        scanf("%lld",&k);
        int ans=0;
        if(k<be-1){
            printf("%lld\n",num[k]+1);
            continue;
        }
        else{
            ans+=pre_sum;k-=(be-1);
            ans+=(nt_sum[0]*(k/lent));
            k%=lent;
            if(k){
                ans+=nt_sum[k];
            }
            printf("%lld\n",ans+1);
        }
    }
    ed=clock();
    return 0;
}

原文地址:http://www.cnblogs.com/I-am-yzh/p/16923135.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性