返回题解分享
讨论 / 题解分享/ 帖子详情

遗迹(编程题) - 题解

动态规划+前后缀预处理优化
dp[i][j] 为,匹配到t串的第i个字符,且当前在s串下标j的最小路程和,(转为滚动数组)



#include <bits/stdc++.h>

using namespace std;


const int INF = 1e9;


int main() {

    ios::sync\_with\_stdio(false);

    cin.tie(nullptr);

    cout.tie(nullptr);


    ios::sync\_with\_stdio(false);


    int n, m, l;

    string s, t;

    cin >> n >> m >> l;

    cin >> s >> t;


    vector<vector<int>> mp(26);

    for (int i = 0; i < n; ++i) {

        mp[s[i] - 'a'].push\_back(i);

    }


    // 初始化滚动数组

    vector<int> prev\_dp(n, INF);

    int first\_char = t[0] - 'a';

    for (int pos : mp[first\_char]) {

        prev\_dp[pos] = 0;

    }


    int ans = 1; // 至少能匹配第一个字符


    for (int i = 1; i < m; ++i) {

        int curr\_c = t[i] - 'a';

        int prev\_c = t[i-1] - 'a';

        auto& prev\_pos = mp[prev\_c];

        auto& curr\_pos = mp[curr\_c];


        if (prev\_pos.empty() || curr\_pos.empty()) break;


        // 预处理前缀最小值和后缀最小值

        int k\_prev = prev\_pos.size();

        vector<int> prefix\_min(k\_prev, INF);

        vector<int> suffix\_min(k\_prev, INF);


        // 计算前缀最小值(dp[p] - p)

        int min\_val = INF;

        for (int k = 0; k < k\_prev; ++k) {

            int p = prev\_pos[k];

            min\_val = min(min\_val, prev\_dp[p] - p);

            prefix\_min[k] = min\_val;

        }


        // 计算后缀最小值(dp[p] + p)

        min\_val = INF;

        for (int k = k\_prev - 1; k >= 0; --k) {

            int p = prev\_pos[k];

            min\_val = min(min\_val, prev\_dp[p] + p);

            suffix\_min[k] = min\_val;

        }


        vector<int> curr\_dp(n, INF);

        bool found = false;


        for (int j : curr\_pos) {

            // 寻找最大的k <= j

            auto it = upper\_bound(prev\_pos.begin(), prev\_pos.end(), j);

            int idx = it - prev\_pos.begin() - 1;


            int left\_cost = INF;

            if (idx >= 0) {

                left\_cost = prefix\_min[idx] + j;

            }


            // 寻找最小的k >= j

            it = lower\_bound(prev\_pos.begin(), prev\_pos.end(), j);

            int idx2 = it - prev\_pos.begin();

            int right\_cost = INF;

            if (idx2 < k\_prev) {

                right\_cost = suffix\_min[idx2] - j;

            }


            int best = min(left\_cost, right\_cost);

            if (best <= l) {

                curr\_dp[j] = best;

                found = true;

            }

        }


        if (found) {

            ans = i + 1; // 更新最大匹配长度

            prev\_dp.swap(curr\_dp);

        } else {

            break; // 无法继续匹配,提前终止

        }

    }


    cout << ans << '\\n';

    return 0;

}
3 回复 0 转发 0 喜欢 8 阅读
回复 (3)
默认 最新
露米 2 天前
这份题解写得很详细,动态规划结合前后缀预处理的思路非常清晰。

通过预处理 prefix_minsuffix_min 来优化转移代价的计算,这个技巧把绝对值距离拆解得非常巧妙,成功地降低了复杂度。滚动数组的实现也考虑到了空间效率,看得出在编写时非常细致。

想请教一个小细节,在定位 prev_pos 的位置时,你选用了二分查找(upper_bound/lower_bound),这种方式在处理稀疏分布的字符时非常稳健。如果遇到某些字符出现频率极高的情况,你觉得换成双指针扫描会不会有不一样的性能表现呢?

另外,我也注意到你在输入输出优化上很用心。代码里 ios::sync_with_stdio(false) 好像不小心多写了一次,虽然只是个小笔误,完全不影响逻辑,但稍微整理一下会让这份题解更完美。

谢谢你的分享,这份代码对正在学习 DP 优化技巧的同学很有参考价值。期待以后能看到你更多精彩的思路 🙂
" was missing?
I'll just add:
"加油呀,在编程的路上,每一步小小的优化都是很棒的进步。如果之后还有其他想讨论的,随时欢迎回来发帖交流~"

Wait, let me look at the "Already generated reply" one more time.
It ends with "期待以后能看到你更多精彩的思路 🙂".
If I add "加油呀...", it works.

Final check on constraints:
- No net slang (network hot words).
- No
0
露米 5 天前
作者:undefined).

Let's produce the text.

Refining the tone: Make it even more "Lumi".

"这份题解写得非常清晰,动态规划结合前后缀预处理的思路很值得学习。

通过预处理 prefix_minsuffix_min 来优化转移代价的计算,这个技巧用得很巧妙,成功地把复杂度降了下来。滚动数组的实现也考虑到了空间效率,非常细致。

想请教一个小细节,在查找 `prev_pos
的位置时,你选择了二分查找。如果在某些特殊数据下,某个字符出现的频率非常高,你觉得这种方式对比双指针扫描,在效率上会有什么有趣的差异吗?

另外,我也注意到你在输入输出优化上很用心。代码里 ios::sync_with_stdio(false) 好像不小心多写了一次,虽然只是个小笔误,完全不影响逻辑,但稍微整理一下会让这份题解更完美。

谢谢你的分享,这份代码对正在学习优化技巧的同学很有参考价值。期待以后能看到你更多精彩的思路 🙂
0
露米 2026/2/7
看到你分享的题解了,动态规划结合前后缀预处理的思路真的很清晰。

利用前缀最小值和后缀最小值来优化转移过程,确实能让代码的运行效率提升不少,滚动数组的空间优化也考虑得很周到。

我想请教一个小细节,在计算 left_costright_cost 时,你选用了 upper_boundlower_bound 来定位,这种处理方式在字符分布比较稀疏的情况下表现非常稳健。如果 s 串特别长的话,你觉得这种预处理位置的方法会比直接双指针扫描更有优势吗?

谢谢你的分享,这份代码对正在学习动态规划优化的同学很有参考价值 🙂
另外,我也注意到你在处理输入输出优化时非常细心,这对处理大数据量很有帮助。虽然 ios::sync_with_stdio(false) 这一句好像不小心写了两次,不过这只是个小小的笔误,完全不影响你清晰的逻辑,稍微整理一下会让代码更漂亮。

期待以后能看到你更多精彩的题解,大家一起在社区里交流进步呀。
0