首页 > 其他分享 >[题解]AT_abc343_g [ABC343G] Compress Strings

[题解]AT_abc343_g [ABC343G] Compress Strings

时间:2024-06-23 13:11:45浏览次数:20  
标签:int 题解 ++ len st re abc343 ABC343G dp

思路

首先假设有两个串 \(a,b\),如果 \(b\) 是 \(a\) 的子串,且 \(a \neq b\) 则不需要考虑 \(b\);如果 \(a = b\),则如需要保留一个 \(a\)。

做完上述操作后,显然最终的答案是由这些串按照一定顺序拼接起来,再删掉重叠部分。

例如:abbccccdde 拼接为 abbccccdde,发现 cc 是重复的,所以删掉重叠部分为 abbccde

显然我们可以使用 KMP 在 \(\Theta(n^2 \cdot |S|)\) 的复杂度内求解出任意两个串 \(s_i,s_j\) 的重叠部分,记作 \(num_{i,j}\)。

于是就变成了经典状压,我们定义 \(dp_{st,i}\) 表示选取串的集合表示为 \(st\),且最后选的一个串为 \(s_j\)。

然后就是经典状压转移了。

Code

#include <bits/stdc++.h>
#define re register

using namespace std;

const int N = 24,M = 4e5 + 10,K = (1 << 20) + 10,inf = 0x3f3f3f3f;
int n,m,ans = inf;
int nxt[M],len[N];
int num[N][N],dp[K][N];
string t[N],s[N];
unordered_map<string,bool> vis;

inline int kmp(string a,string b){
    string s = a + '&' + b; int len = s.length();
    for (re int i = 0;i < len;i++) nxt[i] = 0;
    for (re int i = 1,j = 0;i < len;i++){
        while (j && s[i] != s[j]) j = nxt[j - 1];
        if (s[i] == s[j]) j++;
        nxt[i] = j;
    }
    return nxt[len - 1];
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    memset(dp,inf,sizeof(dp));
    cin >> n;
    for (re int i = 1;i <= n;i++) cin >> t[i];
    for (re int i = 1;i <= n;i++){
        for (re int j = 1;j <= n;j++){
            if (i == j) continue;
            if (t[j].find(t[i]) != string::npos && t[i] != t[j]) goto End;
        }
        if (vis.count(t[i])) continue;
        vis[t[i]] = true;
        s[++m] = t[i]; len[m] = t[i].length();
        End:;
    }
    n = m;
    for (re int i = 1;i <= n;i++){
        for (re int j = 1;j <= n;j++){
            if (i != j) num[i][j] = kmp(s[j],s[i]);
        }
    }
    for (re int st = 1;st < (1 << n);st++){
        int cnt = __builtin_popcount(st);
        if (cnt == 1){
            int x;
            for (re int i = 0;i < n;i++){
                if ((st >> i) & 1){
                    x = i + 1; break;
                }
            }
            dp[st][x] = len[x];
        }
        for (re int i = 0;i < n;i++){
            if (!((st >> i) & 1)) continue;
            for (re int j = 0;j < n;j++){
                if ((st >> j) & 1) continue;
                dp[st | (1 << j)][j + 1] = min(dp[st | (1 << j)][j + 1],dp[st][i + 1] + len[j + 1] - num[i + 1][j + 1]);
            }
        }
    }
    for (re int i = 1;i <= n;i++) ans = min(ans,dp[(1 << n) - 1][i]);
    printf("%d\n",ans);
    return 0;
}

标签:int,题解,++,len,st,re,abc343,ABC343G,dp
From: https://www.cnblogs.com/WaterSun/p/18263288

相关文章

  • [题解]AT_abc342_f [ABC342F] Black Jack
    思路发现自己与庄家的操作是完全独立的,所以考虑分别计算它们。首先考虑自己的情况,定义\(dp_i\)表示掷出骰子的和为\(i\)获胜的概率,并记\(f(i)\)表示\(x=i\)时就不掷的获胜概率。对于每一步我们要么掷骰子(并且掷出的值等概率的在\(1\simD\)中),要么直接结束。两种情......
  • [题解]CF855E Salazar Slytherin's Locket
    思路毒瘤数位DP题。首先,你可以用一个vector储存每一个数字出现的次数,然后用map记忆化。然后可以得到如下TLE#8的代码。因为map自带一只\(\log\)所以,考虑将map优化掉。但是,现在每一种数字可能会出现很多次,所以要用vector维护出现次数,但这样必定需要用map一......
  • [题解]CF666B World Tour
    CSP-2022S2T1弱化版。思路首先因为边权均为\(1\),所以我们可以在\(\Theta(n^2)\)的复杂度用BFS求解出任意两点\(i,j\)的最短距离\(d_{i,j}\)(如果\(i\)不能到达\(j\),则令\(d_{i,j}=-1\))。有一个贪心的结论,就是使每一条\(A\toB,B\toC,C\toD\)的路径长度......
  • [题解]CF622F The Sum of the k-th Powers
    思路首先发现\(\sum_{i=1}^{n}i^k\)是一个\(k+1\)次多项式,那么我们需要求出\(k+2\)个点才能得到唯一的一个\(f(t)=\sum_{i=1}^{t}{i^k}\)。不难通过拉格朗日插值法,将\(x=1\sim(k+2)\)的情况一一带入:\[f(n)=\sum_{i=1}^{k+2}{((\sum_{j=1}^{i}......
  • [题解]CF622D Optimal Number Permutation
    思路首先考虑答案下界,因为\((n-i)\)和\(|d_i+i-n|\)均大于等于\(0\),所以它们相乘一定大于等于\(0\)。于是考虑能不能构造出结果为\(0\)。显然当\(i=n\)时,无论\(d_i\)的值是什么,式子的结果为\(0\)。因此只需要考虑\(i\in[1,n)\)的情况。因为要使结果为......
  • [题解]CF609F Frogs and mosquitoes
    思路发现\(x\)对题目的限制较大,因此考虑先将\(x\)排序并离散化后再来考虑。不难用线段树维护\(\max_{i=l}^{r}\{x_i+len_i\}\),这样我们就可以利用类似线段树上二分的技巧得出是哪一只青蛙吃掉的蚊子。但是有可能有一只蚊子无法吃掉,我们先把它丢进一个集合里面。每有......
  • [题解]CF988D Points and Powers of Two
    思路首先发现选出的数最多\(3\)个,考虑反证法。假设选出了四个数\(a,b,c,d\),并令:\[|a-b|=2^{x_1},|b-c|=2^{x_2},|c-d|=2^{x_3}\]又因为,\(|a-c|,|b-d|\)也都是\(2\)的次幂,那么有\(x_1=x_2=x_3\)。于是\(|a-d|=3\times2^{x_0}\neq2^k\)。在......
  • P9999 [Ynoi2000] tmostnrq 题解
    巨大难写题。就这样一个毒瘤的题,还有人把时空缩小成二分之一放在模拟赛,太好笑了。思路首先将询问离线。我们在\(l_i\)处加入这个点,在\(r_i\)处查询这个点在哪里。那么我们就需要有一个数据结构支持让所有树上的节点一起动。考虑所有点往\(x\)处动。那么对于在\(1\si......
  • LeetCode:经典题之206、92题解及延伸
    系列目录88.合并两个有序数组52.螺旋数组567.字符串的排列643.子数组最大平均数150.逆波兰表达式61.旋转链表160.相交链表83.删除排序链表中的重复元素389.找不同目录系列目录206.反转链表92.反转链表II类和结构体访问修饰符206.反转链表......
  • UNIQUE VISION Programming Contest 2024 Summer (AtCoder Beginner Contest 359) 题
    点我看题A-CountTakahashi没啥好说的点击查看代码#include<bits/stdc++.h>#definerep(i,n)for(inti=0;i<n;++i)#definerepn(i,n)for(inti=1;i<=n;++i)#defineLLlonglong#definefifirst#definesesecond#definepbpush_back#definemprmake_pair......