Good Bye 2015 D. New Year and Ancient Prophecy
一个长为n的数字字符串,划分成若干子串,保证按照递增顺序的方案数。
f[i][j]表示以i下标为结尾,i所在的子串长度不超过j的方案数。可以得到如下递推式:
f[i][j]=f[i][j-1], s[i-j]=='0'
f[i-j][i-j], i-2*j<0
f[i-j][j], s[i-2*j...i-j-1]<s[i-j...i-1]
f[i-j][j-1],s[i-2*j...i-j-1]>=s[i-j...i-1]
那么对于第3、4种情况,需要判定两段字符串的大小,如果从头到尾判断的话,复杂度为O(n),对于极限数据来说会超时。
解决方法有两种:
1. 将s的所有子串哈希,对于两个起始位置x、y,二分找到最小的不相等的长度,然后比较,复杂度为O(logn)
2. 设low[x][y]为:对于两个起始位置x、y的最小的不相等的长度。则有如下递推式:
low[i][j]= 0, s[i-1]!=s[j-1]
low[i+1][j+1]+1, s[i-1]==s[j-1]
然后直接比较x+low,y+low位,复杂度为O(1)
1. 哈希
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <cmath> 6 #include <vector> 7 #include <ctime> 8 #define oo 1000000007 9 #define maxn 5200 10 #define maxm 4200 11 12 using namespace std; 13 14 int n; 15 string s; 16 int f[maxn][maxn]; 17 long long hash[maxn][maxn]; 18 //f[x][y] 表示以x结尾的长度小于等于y的答案 19 20 bool judge(int x,int y,int len) 21 { 22 int l=0,r=len-1,d=0; 23 while (l<=r) 24 { 25 int mid=(l+r)>>1; 26 if (hash[x][x+mid]!=hash[y][y+mid]) 27 { 28 r=mid-1; 29 d=mid; 30 } 31 else 32 l=mid+1; 33 } 34 return s[x+d-1]<s[y+d-1]; 35 } 36 37 int main() 38 { 39 //freopen("D.in","r",stdin); 40 scanf("%d",&n); 41 cin>>s; 42 memset(hash,0,sizeof(hash)); 43 for (int i=1;i<=n;i++) 44 for (int j=i;j<=n;j++) 45 hash[i][j]=hash[i][j-1]*131+s[j-1]; 46 f[0][0]=1; 47 for (int i=1;i<=n;i++) 48 { 49 for (int j=1;j<=i;j++) 50 { 51 if (s[i-j]=='0') 52 { 53 f[i][j]=f[i][j-1]; 54 continue; 55 } 56 int p; 57 //p是前一段可以取得最长长度 58 if (i-2*j>=0) 59 { 60 if (judge(i-2*j+1,i-j+1,j)) p=j; 61 else p=j-1; 62 } 63 else 64 p=i-j; 65 f[i][j]=(f[i][j-1]+f[i-j][p])%oo; 66 } 67 } 68 printf("%d\n",f[n][n]); 69 return 0; 70 }
2.dp
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <cmath> 6 #include <vector> 7 #include <ctime> 8 #define oo 1000000007 9 #define maxn 5200 10 #define maxm 4200 11 12 using namespace std; 13 14 int n; 15 string s; 16 int f[maxn][maxn],low[maxn][maxn]; 17 //f[x][y] 表示以x结尾的长度小于等于y的答案 18 //low[x][y] 表示从x、y开始的最小的不相等的偏移量 19 20 bool judge(int x, int y, int len) 21 { 22 int d=low[x][y]; 23 return d<len && s[x+d-1]<s[y+d-1]; 24 } 25 26 int main() 27 { 28 //freopen("D.in","r",stdin); 29 scanf("%d",&n); 30 cin>>s; 31 for (int i=n;i>=1;i--) 32 for (int j=n;j>i;j--) 33 if (s[i-1]!=s[j-1]) 34 low[i][j]=0; 35 else 36 low[i][j]=low[i+1][j+1]+1; 37 f[0][0]=1; 38 for (int i=1;i<=n;i++) 39 for (int j=1;j<=i;j++) 40 { 41 if (s[i-j]=='0') 42 { 43 f[i][j]=f[i][j-1]; 44 continue; 45 } 46 int p; 47 //p是前一段可以取得最长长度 48 if (i-2*j>=0) 49 { 50 if (judge(i-2*j+1,i-j+1,j)) p=j; 51 else p=j-1; 52 } 53 else 54 p=i-j; 55 f[i][j]=(f[i][j-1]+f[i-j][p])%oo; 56 } 57 printf("%d\n",f[n][n]); 58 return 0; 59 }
AC without art, no better than WA !