Codeforces Round #543 (Div. 2) F dp + 二分 + 字符串哈希
阿新 • • 發佈:2019-03-07
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 + 二分 + 字符串哈希