CSP 第35次认证 2.字符串变换 满分答案

参考:CCF CSP题解:字符串变换(str)(202409-2)_csp字符串变换-CSDN博客
原题目:TUOJ
    针对该参考(第一行链接)代码(一会儿TE80分,一会AC100分),Gemini给出了了超时原因和优化建议:

 超时原因(时间复杂度):

   由于循环周期 T 最大可以达到 63,最坏情况下 k 接近 63。对于 M <= 1000 个查询和 $|s| \le 100$ 的字符串,总复杂度约为$O(M \cdot |s| \cdot L_{\max})$,即$1000 \times 100 \times 63 \approx 630$万次操作。

    虽然 630 万次操作对于现代计算机来说很快,但这不是最优解,尤其是在字符集更大的类似问题中。真正的最优解法应该是:

 更优的解决方案(预存路径,空间换时间):

initFunc 阶段,不仅仅计算周期 $L$,而是直接存储每个字符 $c$ 沿着周期路径变换 $0$$L-1$ 次后的结果。

  1. 预处理path[ch][i] 存储字符 $ch$ 变换 $i$ 次后的结果。

  2. 查询:最终结果字符$= \text{path}[ch][k\pmod{T[ch]}]$

这样,每次查询的字符变换时间复杂度降为 $O(1)$,总复杂度为 $O(M \cdot |s|)$,极大地提高了效率,并且是更优雅的解法。

在官网运行:AC,100分

#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>
using namespace std;

int n;
string str;
unordered_map<int,vector<int>> path;
int T[256] = {0};//int T[256] = {};都可初始化为0
unordered_map<int,int> trans;

void initFunc();
string transFunc(int k);

int main(){
    getline(cin,str);//cin>>str;会在空格处断开!
    str = str.substr(1,size(str)-2);
    cin>>n; cin.ignore();
    initFunc();
    int m;
    cin>>m;
    int k;
    while(m--){
        cin>>k;
        cout<<"#"+transFunc(k)+"#"<<'\n';
    }
}

void initFunc(){
    int n_it = n;
    string tmp;
    while(n_it--){
        getline(cin,tmp);
        char ch_frm = tmp[1];
        char ch_to = tmp[2];
        if(ch_frm != ch_to){//自环不处理
            T[ch_frm] = -1;
            trans[ch_frm] = ch_to;
        }
    }
    for(int i = 0;i < 256;i++){
        if(T[i] != -1) continue;
        int newCh = i;
        path[i].emplace_back(i);
        do{
            newCh = trans[newCh];
            path[i].emplace_back(newCh);
        }while(newCh != i);

        path[i].pop_back();
        T[i] = path[i].size();
    }
}

string transFunc(int k){
    string res = str;
    for(char& ch : res){
        if(!path.count(ch)) continue;//没有映射关系,不处理
        int index = k%T[ch];
        // cout<<ch<<' '<<k<<' ';
        ch = char(path[ch][index]);
        // cout<<ch<<'\n';
    }
    return res;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值