2017ICPC青岛 - J.Suffix

题目虽然假了,但是正解真的非常的好...
题目链接
首先由于当前选取方案会受后一个字符串的选取方案影响,于是我们考虑倒着选取。
那么我们如何知道当前选取多少是最优的呢?答案就是在已经获得优解的字符串基础上,试探性地加上当前枚举的字符

就拿题目中的

3
bbb
aaa
ccc

举例

由于我们是倒着枚举,所以先获得了c的优解。
此时我们枚举aaa,先加上一个a变成ca(因为一定要取一个),之后我们的任务其实就是在ca, caa, caaa里面选取字典序最小的,注意这里其实是倒着的,所以比较的时候也要从尾部开始。如何快速比较两个字符串的字典序大小呢,我们想到了字符串hash + 二分。于是总复杂度就变成了O(NlogN)

更具体的注释都写在代码里了。

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn = 5e5 + 10;

ull pw[maxn], hs[maxn];
const ull base = 131;
char ans[maxn], s[maxn];
int tot;

ull get(int L, int R) {
    return hs[R] - hs[L-1] * pw[R-L+1];
}

 ///二分比较 [x-mid+1, x] 与 [y-mid+1, y] 的字典序大小
bool check(int cur, int can) {
    int L = 1, R = can, len = 0;
    while (L <= R) {
        int mid = L + R >> 1;
        if (get(cur-mid+1, cur) == get(can-mid+1, can)) {
            L = mid + 1;
            len = max(len, mid);
        }
        else {
            R = mid - 1;
        }
    }
    if (can == len) return 0; ///如果LCP等于can的长度,那么肯定是can小,返回0
    return ans[cur-len] < ans[can-len]; ///返回当前枚举是否比can要小
}

int main() {
    int T; scanf("%d", &T);
    pw[0] = 1;
    for (int i = 1; i < maxn; ++ i)
        pw[i] = pw[i-1] * base;
    while (T--) {
        int n; scanf("%d", &n);
        vector<string> rec;
        rec.push_back(""); ///让下标从1开始
        for (int i = 1; i <= n; ++ i) {
            scanf("%s", s);
            rec.push_back(s);
        }
        tot = 0;
        for (int k = n; k >= 1; -- k) {
            string &u = rec[k]; ///用引用减少数组嵌套增加可读性
            u = " " + u; ///下标从1开始
            int len = (int)u.size()-1;
            ans[++tot] = u[len]; ///不管怎么样都要取一个后缀
            hs[tot] = hs[tot-1] * base + u[len];
            int st = tot, add = 1, can = tot; /// can:最远可达位置, st:此时起始位置, add:增量器
            for (int i = len-1; i >= 1; -- i, ++ add) {
                ///暂时更新,反正如果不满足的话会覆盖的
                hs[st+add] = hs[st+add-1] * base + u[i];
                ans[st+add] = u[i];
                if (check(st+add, can)) ///将当前枚举位置与当前最优位置进行比较
                    can = st + add; ///更优则更新
            }
            tot = can; ///将当前位置更新
        }
        for (int i = tot; i >= 1; i --) ///输出
            printf("%c", ans[i]);
        puts("");
    }
    return 0;
}

posted @   ViKyanite  阅读(48)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示
主题色彩