题解: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;
}

浙公网安备 33010602011771号