字典树,贪心-CF566A(好题)

题目大意:给你两堆n个字符串,让你两两配对使得总LCP长度最大.并且需要知道具体方案

题目思路:

贪心:根据LCP从大往小的去匹配.这样一定是最优的.

我想可以利用归纳证明法证明:

首先,若存在两个字符串的 L C P = 字 符 串 长 度 LCP = 字符串长度 LCP=,那么这两个字符串匹配一定是最优.关于这两个字符串的匹配,不会有更大的答案(因为他们本身就这么长了).所以第一步匹配完所有的 L C P = 字 符 串 长 度 LCP = 字符串长度 LCP=的字符串.继续该过程.

…假设枚举到 L C P = i LCP = i LCP=i,假设这两个字符串是 S , T S,T S,T.

由于 L C P = { i + 1 , . . . , m a x l e n } LCP = \{i+1,...,maxlen\} LCP={ i+1,...,maxlen}的情况在这一步之前就处理完了.所以不会存在一对字符串 X , Y X,Y X,Y使得他们和 S , T S,T S,T匹配构成更优解.即这两个匹配是当前最优的.

这个确定了以后,就可以将所有的字符串插入到Trie树.尽量在深度深的地方匹配.维护一个pos[2][maxn]代表树上每个节点所经过的所有字符串的编号.(注意,这里空间复杂度只会到达 字 符 集 长 度 字符集长度 )然后用used[2][maxn]代表某个点是否已经在更深的地方匹配.

然后暴力dfs遍历全Tire树.优先在深度深的地方匹配两堆字符串.并将其打上used标记即可.代码较精简.

#include<bits/stdc++.h>
using namespace std;
const int maxn = 800005;
vector <int> pos[2][maxn];
bool vis[2][100005];
int res[100005];
int ans = 0;
namespace Trie{
    
    
    int tr[maxn][26] , v[maxn] , tot;
    void add (char * s , int t , int id){
    
    
        int u = 0; pos[t][u].push_back(id);
        for (int i = 1 ; s[i] ; i++){
    
    
            if (!tr[u][s[i] - 'a']) tr[u][s[i] - 'a'] = ++tot;
            u = tr[u][s[i] - 'a'];
            pos[t][u].push_back(id);
        }
    }
}
void dfs (int u , int dep)
{
    
    
    using namespace Trie;
    for (int i = 0 ; i < 26 ; i++) if (tr[u][i]) dfs (tr[u][i] , dep + 1);
    vector<int> now[2];
    for (auto g : pos[0][u]) if (!vis[0][g]) now[0].push_back(g);
    for (auto g : pos[1][u]) if (!vis[1][g]) now[1].push_back(g);
    int cnt = min (now[0].size() , now[1].size());
    ans += cnt * dep;
    for (int i = 0 ; i < cnt ; i++) {
    
    
        res[now[0][i]] = now[1][i];
        vis[0][now[0][i]] = vis[1][now[1][i]] = true;
    }
    return ;
}
char tmp[maxn];
int main()
{
    
    
    ios::sync_with_stdio(false);
    int n; cin >> n;
    for (int i = 1; i <= n ; i++){
    
    
        cin >> (tmp + 1);
        Trie::add(tmp , 0 , i);
    }
    for (int i = 1; i <= n ; i++){
    
    
        cin >> (tmp + 1);
        Trie::add(tmp , 1 , i);
    }
    dfs (0 , 0);
    cout << ans << endl;
    for (int i = 1; i <= n ; i++){
    
    
        cout << i << " " << res[i] << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35577488/article/details/109078720