[SCOI2007]壓縮(動態規劃,區間dp,字串雜湊)
阿新 • • 發佈:2018-10-31
[SCOI2007]壓縮
狀態:設\(dp[i][j]\)表示前i個字元,最後一個\(M\)放置在\(j\)位置之後的最短字串長度.
轉移有三類,用刷表法來實現.
第一種是直接往壓縮串後面填字元,這樣就是:
\[dp[i+1][j]=min(dp[i+1][j],dp[i][j]+1)\]
另外一種就是往字串裡新增\(R\),要滿足相鄰兩個字串是匹配的,可以用字串雜湊來快速匹,另外下面的\(k\)和\(sz\)要倍增往後跳(因為重複的串長在成倍增加,題目的樣例解釋的很清楚了).
\[dp[k][j]=min(dp[k][j],dp[k-sz][j]+1)\]
當然還要往後填\(M\)
\[dp[i][i]=min(dp[i][i],dp[i][j])\]
時間複雜度\(O(n^2logn)\)
程式碼很好懂.
#include<bits/stdc++.h> #define ull unsigned long long #define maxn 55 using namespace std; const int base=233; ull ha[maxn],pw[maxn]; char s[maxn]; int n,ans,dp[maxn][maxn]; ull gethash(int l,int r){return ha[r]-ha[l-1]*pw[r-l+1];} int main() { scanf("%s",s+1);n=strlen(s+1);pw[0]=1; for(int i=1;i<=n;i++)ha[i]=ha[i-1]*base+s[i]; for(int i=1;i<=n;i++)pw[i]=pw[i-1]*base; memset(dp,0x3f,sizeof(dp));dp[1][0]=1; //dp[i][j]表示前i個字元,上一個M放在j位置之後的最短加密字串 for(int i=1;i<=n;i++) { for(int j=0;j<i;j++)dp[i][i]=min(dp[i][i],dp[i][j]+1); for(int j=0;j<=i;j++) { dp[i+1][j]=min(dp[i+1][j],dp[i][j]+1);//往後直接新增字母 if(i==j)continue; int sz=i-j;ull haha=gethash(j+1,i); for(int k=i+sz;k<=n;k+=sz)//倍增加R { if(haha==gethash(k-sz+1,k))//雜湊匹配字串 dp[k][j]=min(dp[k][j],dp[k-sz][j]+1); else break; haha=haha*pw[sz]+haha;sz<<=1; } } } ans=1e9; for(int j=0;j<n;j++)ans=min(ans,dp[n][j]); cout<<ans<<endl; return 0; }