当前位置:首页 > 洛谷 > 洛谷P2412题解:后缀树高效解决字符串比较问题

洛谷P2412题解:后缀树高效解决字符串比较问题

2个月前 (08-15)

洛谷P2412题解:后缀树高效解决字符串比较问题 洛谷题解 后缀树 ST结构构建 ST表 字典序 第1张

一、题目解读

洛谷P2412题要求处理一组字符串,并支持区间内字典序最小字符串的查询。题目关键在于高效处理字符串比较与查找,传统暴力方法时间复杂度无法满足需求,需引入更优的数据结构

二、解题思路

采用**后缀树ST表)**解决。核心思路为:

1. 预处理:将输入字符串转为小写,避免重复字符转换,减少比较开销。

2. 构建ST表:利用倍增思想,通过logN级预处理,保存每个区间的最小值索引。

3. 查询优化:利用二分查找原理,通过ST表快速定位区间内字典序最小的字符串索引。

三、解题步骤

1. 输入处理:读取N个字符串,存储原版与预处理小写版本。

2. ST表构建:

○ 初始化第一层级(log2(N)级):直接记录每个位置的原索引。

○ 递进层级:通过比较相邻区间的字典序最小值,合并生成更高层索引。

3. 查询实现:

○ 计算查询区间长度对应的ST层级j。

○ 比较区间两端对应层级的最小值,返回字典序更小者。

四、代码与注释

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;

// 构建后缀(ST表)
vector<vector<int>> buildST(const vector<string>& words) {
    int n = words.size();
    int k = log2(n) + 1; // 层级数量
    vector<vector<int>> st(n, vector<int>(k)); // 存储各层级区间最小值索引
    
    // 预处理小写版本避免重复转换
    vector<string> lower_words(n);
    for(int i = 0; i < n; ++i) {
        lower_words[i].resize(words[i].size());
        transform(words[i].begin(), words[i].end(), lower_words[i].begin(), ::tolower);
    }

    // 初始化第一层级(每个位置即为自身最小值)
    for(int i = 0; i < n; ++i) st[i][0] = i;
    
    // 递进构建高层级
    for(int j = 1; (1<<j) <= n; ++j) { // 遍历层级
        for(int i = 0; i + (1<<j) - 1 < n; ++i) { // 遍历起始位置
            int x = st[i][j-1], y = st[i+(1<<(j-1))][j-1];
            // 比较两个子区间的最小值(考虑字典序相同则取索引小者)
            st[i][j] = (lower_words[x] > lower_words[y] || (lower_words[x] == lower_words[y] && x > y))? x : y;
        }
    }
    return st;
}

// 查询区间字典序最小值索引
int query(const vector<vector<int>>& st, const vector<string>& words, const vector<string>& lower_words, int l, int r) {
    int len = r - l + 1;
    int j = log2(len); // 对应层级
    int x = st[l][j], y = st[r - (1<<j) + 1][j];
    return (lower_words[x] > lower_words[y] || (lower_words[x] == lower_words[y] && x > y))? x : y;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m;
    cin >> n >> m; // 输入字符串数量与查询次数
    vector<string> words(n);
    for(int i = 0; i < n; ++i) cin >> words[i];
    
    auto st = buildST(words);
    vector<string> lower_words(n);
    for(int i = 0; i < n; ++i) {
        lower_words[i].resize(words[i].size());
        transform(words[i].begin(), words[i].end(), lower_words[i].begin(), ::tolower);
    }
    
    while(m--) {
        int x, y;
        cin >> x >> y;
        x--; y--;
        int pos = query(st, words, lower_words, x, y);
        cout << words[pos] << '\n';
    }
    
    return 0;
}

五、总结

本解法通过后缀树(ST表)将区间查询优化至O(logN),结合预处理与二分思想,显著降低时间复杂度。适用于需频繁查询区间最值的场景,代码中细节(如小写预处理、字典序比较逻辑)体现了优化技巧,为同类问题提供高效解决方案。

原创内容 转载请注明出处

分享给朋友:

相关文章

洛谷P1121题解:动态规划求解环形数组最大子段和问题(附代码注释)

洛谷P1121题解:动态规划求解环形数组最大子段和问题(附代码注释)

一、题目解读洛谷P1121题要求求解环形数组的最大子段和,即在一个环形数组中找到一个连续子段,使其元素和最大。环形数组的特殊性在于首尾元素可相连,需考虑线性子段与跨越首尾的环形子段两种情况。二、解题思...

洛谷P3694题解:动态规划与状态压缩优化解题全解析

洛谷P3694题解:动态规划与状态压缩优化解题全解析

一、题目解读洛谷P3694题要求解决一个多团队人数分配问题,给定n个元素和m个团队,每个元素属于一个团队,需将元素重新排列,使相邻元素属于不同团队,并最小化调整次数。题目难点在于如何高效处理团队间的空...

洛谷P2789题解:递归算法与避免重复计算的技巧

洛谷P2789题解:递归算法与避免重复计算的技巧

一、题目解读洛谷P2789题要求计算n条直线在平面上两两相交产生的交点总数。题目强调交点不重复,需考虑平行线情况。关键点在于如何高效枚举所有可能的交点组合,并排除重复结果。二、解题思路采用递归算法,核...

洛谷P1438题解:基于线段树的等差数列

洛谷P1438题解:基于线段树的等差数列

一、题目解读洛谷P1438题要求处理数列的区间更新与单点查询操作,其中更新方式为给定区间内元素按等差数列递增。传统暴力修改会因多次区间遍历导致超时,需设计高效数据结构——线段树,结合等差数列性质实现O...

力扣1643题:第K小字典序路径(附C++代码与解题思路)

力扣1643题:第K小字典序路径(附C++代码与解题思路)

一、题目解读本题要求生成从原点(0, 0)到目标坐标(destination[0], destination[1])的路径中,字典序第K小的路径。路径仅由向下字符'V'(代表垂直移动)...

洛谷P1616题解:动态规划之完全背包问题

洛谷P1616题解:动态规划之完全背包问题

一、题目解读洛谷P1616题要求在一个包含M个活动(每个活动有固定时间和价值)的场景中,求解在总时间T内选择活动的最优组合,使得总价值最大化。活动可重复选择,需利用动态规划算法找到最优解。题目强调时间...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。