洛谷试炼日记(trie树,AC自动机,回文自动机)(解题报告)

Trie树

P4683 [IOI2008] Type Printer 打印机

题目链接
就是求最小操作数嘛 一开始想太多,其实就是最长的最后打印呗
那就先标记一下最长的那一串 然后dfs先跳过那些最长串经过的节点
最后再枚举最长串的节点就行啦
细节看代码(也没啥细节啦
AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 5e6 + 10;
int ch[N][26],cnt,fl[N],mx[N];
string s,a,ss;char t[N];
int n,tot;

template <class T>
inline void read(T& x){
    x = 0;int f = 1;
    char c = getchar();
    while(!isdigit(c)){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48),c = getchar();
    x *= f;
}

void insert(string s){
    int u = 0;
    for(int i = 0;s[i];i ++){
        int v = s[i] - 'a';
        if(!ch[u][v]) ch[u][v] = ++ cnt;
        u = ch[u][v];
        t[u] = v + 'a';
    }
    fl[u] = 1;
}

void dfs(string a){
    int u = 0;
    for(int i = 0;a[i];i ++){
        u = ch[u][a[i] - 'a'];
        mx[u] = 1;
    }
}

void solve(int u){
    if(fl[u]){
        tot ++;
        ss += 'P';
    }
    if(tot == n){
        cout << ss.size() << endl;
        for(int i = 0;ss[i];i ++){
            cout << ss[i] << endl;
        }
        return;
    }

    for(int i = 0;i < 26;i ++){
        if(ch[u][i] && !mx[ch[u][i]]){
            ss += t[ch[u][i]];
            solve(ch[u][i]);
            ss += '-';
        }
    }
    for(int i = 0;i < 26;i ++){
        if(ch[u][i] && mx[ch[u][i]]){
            ss += t[ch[u][i]];
            solve(ch[u][i]);
            ss += '-';
        }
    }
}
int main(){
    read(n);
    int mxl = 0;
    for(int i = 0;i < n;i ++){
        cin >> s;
        insert(s);
        if(mxl < s.size()){
            mxl = s.size();
            a = s;
        }
    }
    dfs(a);
    solve(0);
    return 0;
}

AC自动机

P2444 [POI2000]病毒

题目链接
fail跳来跳去挺好玩的,不这道题咱不跳fail
我们标记一下病毒串,然后在trie图上判断是否存在一个环,且环上没有病毒标记
dfs判环就行啦
AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;
const int N = 3e5 + 10;
int ch[N][2],cnt,fail[N],fl[N];
bool vis[N],in[N],flag;
char s[N];
void insert(char s[]){
    int u = 0;
    for(int i = 0;s[i];i ++){
        bool v = s[i] - '0';
        if(!ch[u][v]) ch[u][v] = ++ cnt;
        u = ch[u][v];
    }
    fl[u] = 1;
}

void getfail(){
    queue<int> q;
    for(int i = 0;i < 2;i ++) if(ch[0][i]) q.push(ch[0][i]);
    while(!q.empty()){
        int u = q.front();q.pop();
        for(int i = 0;i < 2;i ++){
            if(ch[u][i]){
                fail[ch[u][i]] = ch[fail[u]][i];
                fl[ch[u][i]] |= fl[fail[ch[u][i]]];
                q.push(ch[u][i]);
            }
            else ch[u][i] = ch[fail[u]][i];
        }
    }
}

void dfs(int u){
    if(in[u]){
        flag = 1;
        return;
    }
    if(vis[u] || fl[u]) return;
    in[u] = vis[u] = 1;
    dfs(ch[u][0]);
    dfs(ch[u][1]);
    in[u] = 0;
}
int n;
int main(){
    scanf("%d",&n);
    for(int i = 0;i < n;i ++){
        scanf("%s",s);
        insert(s);
    }
    getfail();
    flag = 0;
    dfs(0);
    if(flag) puts("TAK");
    else puts("NIE");
    return 0;
}

回文自动机

P5496 【模板】回文自动机(PAM)

题目链接
就是一模板题嘞 封装写起来好舒服
AC代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 2e6 + 10;

struct PAM_t{
    int ch[26],fail,len,num;
};

struct PAM{
    PAM_t b[N];
    char s[N];
    int cur,n,cnt;
    PAM(){
        b[0].len = 0,b[1].len = -1;
        b[0].fail = 1,b[1].fail = 0;
        cnt = 1;
        cur = 0;
    }
    void read(){
        scanf("%s",s);
    }
    int getfail(int x){
        while(s[n - b[x].len - 1] != s[n] || n - b[x].len - 1 < 0) x = b[x].fail;
        return x;
    }

    void insert(){
        int pre = getfail(cur);
        if(!b[pre].ch[s[n]]){
            b[++ cnt].len = b[pre].len + 2;
            int tmp = getfail(b[pre].fail);
            b[cnt].fail = b[tmp].ch[s[n]];
            b[cnt].num = b[b[cnt].fail].num + 1;
            b[pre].ch[s[n]] = cnt;
        }
        cur = b[pre].ch[s[n]];
    }

    void solve(){
        int k = 0;
        for(n = 0;s[n];n ++){
            s[n] = (s[n] - 97 + k) % 26 + 97;
            s[n] -= 'a';
            insert();
            printf("%d ",b[cur].num);
            k = b[cur].num;
        }
    }
}P;
int main(){
    P.read();
    P.solve();
    return 0;
}

P3649 [APIO2014]回文串

题目链接
也是模板啦 就是要处理一下出现次数而已
和AC自动机的处理方式很像的
记得在外面把短的回文串出现次数加上长串出现次数就行啦(前提是短串是长串的子串,fail指向的不就是最长回文后缀吗,后缀不就是子串了吗)
AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>

using namespace std;
const int N = 2e6 + 10;
struct PAM_T{
    int ch[26],fail,len,num;
};

struct PAM{
    PAM_T b[N];
    char s[N];
    int cnt,n,cur;
    PAM(){
        b[0].len = 0,b[1].len = -1;
        b[0].fail = 1,b[1].fail = 0;
        cnt = 1;
        cur = 0;
    }
    void read(){
        scanf("%s",s);
    }
    int getfail(int x){
        while(s[n - b[x].len - 1] != s[n]) x = b[x].fail;
        return x;
    }
    void insert(){
        int pre = getfail(cur);
        if(!b[pre].ch[s[n]]){
            b[++ cnt].len = b[pre].len + 2;
            int tmp = getfail(b[pre].fail);
            b[cnt].fail = b[tmp].ch[s[n]];
            b[pre].ch[s[n]] = cnt;
        }
        cur = b[pre].ch[s[n]];
        b[cur].num ++;
    }
    void solve(){
        for(n = 0;s[n];n ++){
            s[n] -= 'a';
            insert();
        }
        long long ans = 0;
        for(int i = cnt;i;i --){
            b[b[i].fail].num += b[i].num;
            if(b[i].num * 1ll * b[i].len > ans) ans = b[i].num * 1ll * b[i].len;
        }
        printf("%lld\n",ans);
    }
}P;
int main(){
    P.read();
    P.solve();
    return 0;
}

P4287 [SHOI2011]双倍回文

题目链接
需要多维护一个trans数组,用来表示长度小于或等于当前节点长度一半的最长回文后缀 更新方法和fail数组大同小异
新建一个节点后,如果这个节点的长度<=2,那么这个节点的trans直接指向这个节点的fail(因为fail的长度肯定<=1)
否则,就跳fail,判断是否能够扩展以及长度是否满足(和fail更新差不多)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 5e5 + 10;
int ch[N][26],fail[N],len[N],trans[N];
int n,cnt,ans,cur;
char s[N];
int getfail(int x,int i){
    while(s[i - len[x] - 1] != s[i]) x = fail[x];
    return x;
}

void build(){
    for(int i = 0;s[i];i ++){
        int x = s[i] - 'a';
        int p = getfail(cur,i);
        if(!ch[p][x]){
            len[++ cnt] = len[p] + 2;
            fail[cnt] = ch[getfail(fail[p],i)][x];
            ch[p][x] = cnt;
            if(len[cnt] <= 2) trans[cnt] = fail[cnt];
            else{
                int tmp = trans[p];
                while(s[i - 1 - len[tmp]] != s[i] || (len[tmp] + 2) * 2 > len[cnt]) tmp = fail[tmp];
                trans[cnt] = ch[tmp][x];
            }
        }
        cur = ch[p][x];
    }
}
int main(){
    scanf("%d",&n);
    scanf("%s",s);
    fail[0] = 1,fail[1] = 0;
    len[0] = 0,len[1] = -1;
    cur = 0,cnt = 1;
    build();
    for(int i = 2;i <= cnt;i ++){
        if((len[trans[i]] * 2) == len[i] && len[i] % 4 == 0)
            ans = max(ans,len[i]);
    }
    printf("%d\n",ans);
    return 0;
}

以后有机会会写篇回文自动机的博客的(毕竟它比较难)
主要是后缀数组还没学透啊啊啊啊啊好难

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码下载:完整代码,可直接运行 ;运行版本:2022a或2019b或2014a;若运行有问题,可私信博主; **仿真咨询 1 各类智能优化算法改进及应用** 生产调度、经济调度、装配线调度、充电优化、车间调度、发车优化、水库调度、三维装箱、物流选址、货位优化、公交排班优化、充电桩布局优化、车间布局优化、集装箱船配载优化、水泵组合优化、解医疗资源分配优化、设施布局优化、可视域基站和无人机选址优化 **2 机器学习和深度学习方面** 卷积神经网络(CNN)、LSTM、支持向量机(SVM)、最小二乘支持向量机(LSSVM)、极限学习机(ELM)、核极限学习机(KELM)、BP、RBF、宽度学习、DBN、RF、RBF、DELM、XGBOOST、TCN实现风电预测、光伏预测、电池寿命预测、辐射源识别、交通流预测、负荷预测、股价预测、PM2.5浓度预测、电池健康状态预测、水体光学参数反演、NLOS信号识别、地铁停车精准预测、变压器故障诊断 **3 图像处理方面** 图像识别、图像分割、图像检测、图像隐藏、图像配准、图像拼接、图像融合、图像增强、图像压缩感知 **4 路径规划方面** 旅行商问题(TSP)、车辆路径问题(VRP、MVRP、CVRP、VRPTW等)、无人机三维路径规划、无人机协同、无人机编队、机器人路径规划、栅格地图路径规划、多式联运运输问题、车辆协同无人机路径规划、天线线性阵列分布优化、车间布局优化 **5 无人机应用方面** 无人机路径规划、无人机控制、无人机编队、无人机协同、无人机任务分配 **6 无线传感器定位及布局方面** 传感器部署优化、通信协议优化、路由优化、目标定位优化、Dv-Hop定位优化、Leach协议优化、WSN覆盖优化、组播优化、RSSI定位优化 **7 信号处理方面** 信号识别、信号加密、信号去噪、信号增强、雷达信号处理、信号水印嵌入提取、肌电信号、脑电信号、信号配时优化 **8 电力系统方面** 微电网优化、无功优化、配电网重构、储能配置 **9 元胞自动机方面** 交通流 人群疏散 病毒扩散 晶体生长 **10 雷达方面** 卡尔曼滤波跟踪、航迹关联、航迹融合

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值