1. 程式人生 > >Codeforces Round #543 (Div. 2) F dp + 二分 + 字符串哈希

Codeforces Round #543 (Div. 2) F dp + 二分 + 字符串哈希

min test char s mod space else ORC 就是 復雜

https://codeforces.com/contest/1121/problem/F

題意

給你一個有n(<=5000)個字符的串,有兩種壓縮字符的方法:
1. 壓縮單一字符,代價為a
2. 壓縮一個串,條件是這個串是前面整個串的連續子串,代價為b

題解

  • n<=5000
  • 定義dp[i]為壓縮前i個字符的代價,則答案為dp[n]
  • dp[i]=min(dp[i-1]+a,min(dp[j]+b)(即[j+1,i]為[1,j]的子串))
  • 用字符串哈希處理判定一個串是否為前面的子串

坑點

  • 串abab,如何判定後面一個ab是前面ab的子串?
    • 照舊給每一個位置加權
    • 判定的時候,首先將權值加到同一個權級再比較

      比如存在一個首字符在i和一個首字符在j的串,那麽比較的時候哈希值分別都要乘以(size-i)和(size-j),得到權級都是size的串

  • 兩層for已經是n*n復雜度,還需要判定後面的串是否是前面串的子串?
    • 一開始想法就是用一個map[i]記錄每個位置之前哈希值的出現次數,但是會超內存
    • 換一個\(n*n*log(n)\)的算法,二分找出最小的位置\(log(n)\),枚舉前面每一個位置固定長度串\(n\)

代碼

#include<bits/stdc++.h>
#define P 47               //加權的質數較小
#define mod 1000000003     //哈希表的質數較大
#define ll long long 
using namespace std;
int n,a,b,i,j,sum[5005],dp[5005],pw[6000],l,mid,r;
char s[5005];

void init(){
    pw[0]=1;
    for(int i=1;i<=n+100;i++)
        pw[i]=(ll)pw[i-1]*P%mod;
    for(int i=1;i<=n;i++)
        sum[i]=(sum[i-1]+(ll)pw[i]*(s[i]-'a'+1)%mod)%mod;
}

int geths(int i,int j){
    return (int)(((ll)sum[j]-sum[i-1]+mod)%mod*pw[n+50-i]%mod);
}

int ck(int p,int i,int j,int len){
    int hs=geths(i,j);
    for(int k=1;k<=p-len+1;k++){
        if(geths(k,k+len-1)==hs)return 1;
    }
    return 0;
}

int main(){
    cin>>n>>a>>b;
    scanf("%s",s+1);
    init(); 
    dp[0]=0;
    for(i=1;i<=n;i++){
        dp[i]=dp[i-1]+a;
        l=1;r=i-1;
        while(l<r){
            mid=(l+r)/2;
            if(ck(i-mid,i-mid+1,i,mid))l=mid+1;
            else r=mid;
        }
        while(!ck(i-l,i-l+1,i,l))l--;
        if(l>=1)dp[i]=min(dp[i-l]+b,dp[i]);
    }
    cout<<dp[n];
}

Codeforces Round #543 (Div. 2) F dp + 二分 + 字符串哈希