【经典dp】hdu4622Reincarnation
呕 卡64M内存卡了好久
题目描述
题目大意
给出一个字符串 S,每次询问一个区间的本质不同的子串个数。$|S| \le 2000$.
题目分析
首先无脑$n^2$个set开起来:MLE
稍微想想这个东西是能够区间dp的:对于一个字符串$[l,r]$,它的存在首先给所有包含$[l,r]$的$f[i][j]$贡献$1$;考虑相同的字符串下一次出现的位置$[l',r']$,那么对所有包含$[l,r']$的$f[i][j]$贡献$-1$。最后再按照顺序累加一下即可。
第一次:std::map存一个哈希值上一次出现的位置,MLE
第二次:手写哈希表,$\mod 8000007$挂链,MLE
第三次:手写哈希表,$\mod 5000007$挂链,MLE
第四次:手写哈希表,$\mod 10007$挂链,TLE?
第五次:手写哈希表,$\mod 500007$挂链、查询时候顺带后续修改,MLE???
嗯?这啥玩意?
啧。后来意识到枚举不同长度的时候,哈希表里历史记录值都是没用的。所以每次枚举长度都应该重置哈希表。而且这样一来,哈希表模数和空间就可以开小了。
还听说这个经典问题有一个$O(n)$做法?
网上找了一下只有两个类似的是:
1.loj#6070. 「2017 山东一轮集训 Day4」基因 强制在线询问$s[l\cdots r]$中有多少本质不同的回文子串 $n\le 10^5,q\le 2\times10^5$
2.「湖南省队集训2018 Day2」有趣的字符串题 每个询问会询问一段区间的本质不同回文子串个数 $n\le 3\times 10^5,m\le 10^6$
不过这两个题都要用到回文子串的性质:“一个串的所有回文后缀可以被划分成不超过log个等差数列”。所以网上尚没有找到这个问题的$O(n)$解法?
1 #include<bits/stdc++.h> 2 typedef unsigned long long uint; 3 const int maxn = 2035; 4 const int maxp = 2035; 5 uint base = 2333; 6 7 struct node 8 { 9 uint num; 10 int val; 11 node(uint a=0, int b=0):num(a),val(b) {} 12 }edges[maxp]; 13 struct Hash_Table 14 { 15 #define MO 10007 16 int head[10035],nxt[maxp],edgeTot; 17 void init(){edgeTot=0,memset(head, -1, sizeof head);} 18 int query(uint x, int c) 19 { 20 for (int i=head[x%MO]; i!=-1; i=nxt[i]) 21 if (edges[i].num==x){ 22 std::swap(c, edges[i].val); 23 return c; 24 } 25 edges[++edgeTot] = node(x, c), nxt[edgeTot] = head[x%MO], head[x%MO] = edgeTot; 26 return 0; 27 } 28 #undef MO 29 }g; 30 int T,n,q,l,r; 31 char s[maxn]; 32 int f[maxn][maxn]; 33 uint pwr[maxn],hsh[maxn],val; 34 35 uint hash(int l, int r) 36 { 37 return hsh[r]-hsh[l-1]*pwr[r-l+1]; 38 } 39 int main() 40 { 41 // freopen("hdu4622.in","r",stdin); 42 // freopen("hdu4622.out","w",stdout); 43 pwr[0] = 1; 44 for (int i=1; i<=2000; i++) 45 pwr[i] = pwr[i-1]*base; 46 for (scanf("%d",&T); T; --T) 47 { 48 scanf("%s",s+1); 49 n = strlen(s+1); 50 for (int i=1; i<=n; i++) hsh[i] = hsh[i-1]*base+s[i]-'a'+1; 51 for (int i=1; i<=n; i++) 52 for (int j=i; j<=n; j++) f[i][j] = 0; 53 for (int d=1; d<=n; d++) 54 { 55 g.init(); 56 for (int i=1,pos; i+d-1<=n; i++) 57 { 58 val = hash(i, i+d-1), pos = g.query(val, i); 59 if (pos) --f[pos][i+d-1]; 60 ++f[i][i+d-1]; 61 } 62 } 63 for (int i=n; i>=1; i--) 64 for (int j=i; j<=n; j++) 65 f[i][j] += f[i+1][j]+f[i][j-1]-f[i+1][j-1]; 66 for (scanf("%d",&q); q; --q) 67 scanf("%d%d",&l,&r), printf("%d\n",f[l][r]); 68 } 69 return 0; 70 }
END