CF1200E Compress Words 题解

CF1200E Compress Words 题解

题目链接:CF1200E Compress Words

题意:给定一堆字符串,依次插入答案串尾部,每次删掉答案串的后缀 与 待插入串的前缀的最大匹配串

解法一 KMP

这个解法常数比较大

可以想到,暂时把待插入串放到答案串前,然后跑一遍KMP

最后一个值就是新串的最长前后缀,也就是我们要求的最大匹配串的长度

由于直接拼接新串会导致某些时候答案超出原长

因此我们要在它们拼接的地方加上一些非字符集的字符或者乱七八糟的字符

注意不能自带border

而这样的算法还是不行的,因为答案串中有很多字符其实用不到

因此我们只要截取答案串后面的拼接即可

时间复杂度 O ( ∑ ∣ s i ∣ ) = O ( n ) O(\sum |s_i|) = O(n) O(si)=O(n)

代码如下

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(2e6+5)
int n,fail[N],len1,len2;
char ans[N],s[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin >> n;
    for(int i=1; i<=n; i++)
    {
        cin >> s+1;len2=strlen(s+1);
        int minlen=min(len1,len2);
        string str="0";
        for(int i=1; i<=minlen; i++)
            str+=s[i];
        str+="!@#$%^&*";
        for(int i=len1-minlen+1; i<=len1; i++)
            str+=ans[i];
        for(int i=2,j=0; i<=minlen*2+8; i++)
        {
            while(j&&str[i]!=str[j+1])j=fail[j];
            if(str[i]==str[j+1])++j;
            fail[i]=j;
        }
        for(int i=fail[minlen*2+8]+1; i<=len2; i++)
            ans[++len1]=s[i];
    }
    cout << ans+1 << endl;
    return 0;
}

解法二 字符串哈希

这里主要扯扯字符串哈希的东西

虽然之前没怎么研究,一直用的unordered_map<string,int>mp

但是这次就手写个字符串哈希吧

一般字符串哈希有两种写法,这里就按照OI WIKI默认的写法来讲了

定义哈希函数 f ( s ) = ∑ i = 1 l s [ i ] × b l − i m o d    M f(s) = \sum\limits_{i=1}^{l}s[i]\times b^{l-i} \mod M f(s)=i=1ls[i]×blimodM

一般来讲我们不会只用一个大质数 M M M ,而是一堆 qwq

因为这样它的 n n n 次比较的错误率会降到 1 − ( 1 − 1 ∏ M i ) n 1-\left(1-\dfrac{1}{\prod M_i}\right)^n 1(1Mi1)n

这个数有多小呢?我们假设只有两个大质数 1e9+7,998244353,比较1e6

那么它的值约为 0.00000000000100175873 0.00000000000100175873 0.00000000000100175873

这错误的概率还没计算机出故障的几率高吧

因此我们直接忽略不计

那么对于一个字符串xyz,它的值就等于 ( x b 2 + y b + z ) m o d    M (xb^2+yb+z) \mod M (xb2+yb+z)modM

类比于一个 b b b 进制的数,应该还算比较好理解的

根据这个式子,如果我们要求它的一个子串 s [ l … r ] s[l\dots r] s[lr] 的哈希值怎么办

注意到 f ( s [ l … r ] ) = ∑ i = l r s [ i ] × b r − i m o d    M f(s[l\dots r]) = \sum\limits_{i=l}^{r}s[i]\times b^{r-i} \mod M f(s[lr])=i=lrs[i]×brimodM

因此可以得到结论 f ( s [ l … r ] ) = f ( s [ 1 … r ] ) ) − f ( s [ 1 … l − 1 ] ) × b r − l + 1 m o d    M f(s[l\dots r]) = f(s[1\dots r]))-f(s[1\dots l-1])\times b^{r-l+1} \mod M f(s[lr])=f(s[1r]))f(s[1l1])×brl+1modM

注意这里,由于做了取模操作, f ( s [ l … r ] ) f(s[l\dots r]) f(s[lr]) 的值可能为负数,要注意把它转成正的

这样我们就可以 O ( n ) O(n) O(n) 预处理 b b b O ( 1 ) O(1) O(1) 查询了(也可以就快速幂 O ( log ⁡ n ) O(\log n) O(logn) 查询)

好的相信你已经理解了字符串哈希的一些基本东西,下面来做个简单的小例题

好吧就是这题,看它题意

很简单,直接暴力枚举+哈希就OK了,

而朴素的解法,语句 str1==str2,它实质上是 O ( n ) O(n) O(n) 级别的

因此我们把朴素解法的 O ( n 2 ) O(n^2) O(n2) 就可以压到 O ( n ) O(n) O(n)

代码如下

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(1e6+5)
#define hash_cnt 2
const int hash_base[hash_cnt]={29,31};
const int hash_mod[hash_cnt]={1000000007,998244353};
struct hash_str
{
    char s[N];
    int len,hsh[hash_cnt][N];
    int pwd[hash_cnt][N];
    
    void init()
    {
        len=0;
        for(int i=0; i<hash_cnt; i++)
        {
            hsh[i][0]=0;
            pwd[i][0]=1;
        }
    }
    hash_str(){init();}
    void add(char ch)
    {
        s[++len]=ch;
        for(int i=0; i<hash_cnt; i++)
        {
            pwd[i][len]=pwd[i][len-1]*hash_base[i]%hash_mod[i];
            hsh[i][len]=(hsh[i][len-1]*hash_base[i]+ch)%hash_mod[i];
        }
    }
    vector<int> gethash(int l,int r)
    {
        vector<int> res(hash_cnt,0);
        for(int i=0; i<hash_cnt; i++)
        {
            int t=(hsh[i][r]-hsh[i][l-1]*pwd[i][r-l+1])%hash_mod[i];
            t=(t+hash_mod[i])%hash_mod[i];
            res[i]=t;
        }
        return res;
    }
}s,t;
bool equal(const vector<int> &vec1,const vector<int> &vec2)
{
    assert(vec1.size()==vec2.size());
    for(int i=0; i<vec1.size(); i++)
        if(vec1[i]!=vec2[i])return 0;
    return 1;
}
int n;
char str[N];
void solve(char *str)
{
    int len=strlen(str+1);
    t.init();
    for(int i=1; i<=len; i++)
        t.add(str[i]);
    int d=1;
    for(int j=min(len,s.len); j>=1; j--)
        if(equal(t.gethash(1,j),s.gethash(s.len-j+1,s.len)))
            {d=j+1;break;}
    for(int i=d; i<=len; i++)
        s.add(str[i]);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin >> n;
    for(int i=1; i<=n; i++)
    {
        cin >> str+1;
        solve(str);
    }
    cout << s.s+1 << endl;
    return 0;
}

转载请说明出处

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值