题解:luogu P1666 前缀单词

题目链接:https://www.luogu.com.cn/problem/P1666

单调性:将字符串按字典序升序排序。

这样有一个关键性质:对于一个子集 \(S'\),其中最大字符串的下标为 \(j\),如果它是安全的,且对于 \(i>j\),\(s_j\) 不是 \(s_i\) 的前缀,那么子集 \(S'\cup i\) 也是安全的。

证明: 若加入 \(i\) 后子集不安全,那么一定存在 \(k\neq j\) 使得 \(s_k\) 是 \(s_i\) 的前缀。这与排序后的单调性矛盾,故子集一定安全。

设计 dp,\(f_i\) 表示 \(i\) 为集合内最大单词下标的安全集合数量,转移是比较容易的:

\[f_i=\sum_{j<i}f_j \]

其中 \(j\) 满足 \(s_j\) 不是 \(s_i\) 的前缀。

答案是 \(\sum f_i+1\),不要忘了加上空串。

dp \(O(n^2)\),判断前缀也可以直接暴力 \(O(n)\),总时间复杂度 \(O(n^3)\)。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, ans;
inline bool check(int i, int j, const vector<string> &s){
    for(int k = 0; k < s[i].size(); k++)
        if(s[i][k] != s[j][k]) return false;
    return true;
}
signed main(){
    cin >> n;
    vector<string> s(n);
    for(int i = 1; i <= n; i++) cin >> s[i - 1];
    sort(s.begin(), s.end());
    vector<int> f(n, 1);
    for(int i = 0; i < n; i++){
        for(int j = 0; j < i; j++)
            if(!check(j, i, s))
                f[i] += f[j];
        ans += f[i];
    }
    cout << ans + 1 << '\n';
    return 0;
}
posted @ 2026-08-22 17:15  Antony_pico  阅读(9)  评论(0)    收藏  举报