jzoj5462
題意求長度為n的字符串中的長度為m的連續子串有多少個是不同的。
比如n=5, s=aaaab
它長度為3的子串有
aaa、aaa、aab
有兩個不同的子串,答案為2。
解法有兩種,其一是hash,其二是后綴自動機。
這里講講hash。
我用的雙hash。
大意就是第一個hash用來查詢,第二個hash用來判第一個hash的沖突(沖突的概率極小)
hash的寫法:
預處理:
h1[0] = h2[0] = 0;for (int i = 1; i <= len; i++){h1[i] = (h1[i - 1] * 131 + s[i]) % m1;h2[i] = (h2[i - 1] * 97 + s[i]) % m2;}求出每個子串的hash值:
for (LL i = 1; i <= len - m + 1; i++){LL k1 = ((h1[i + m -1] - h1[i - 1] * ksm(131, m, m1)) % m1 + m1) % m1;LL k2 = ((h2[i + m -1] - h2[i - 1] * ksm(97, m, m2)) % m2 + m2) % m2;hash(k1, k2);}如上圖所示
最后統計答案:
void add(LL k1, LL k2) {a[tot].x = k2;a[tot].next = h[k1];h[k1] = tot++; }void hash(LL k1, LL k2) {for (LL i = h[k1]; ~i; i = a[i].next)if (a[i].x == k2)return;add(k1, k2);ans++; }如果發現兩個hash值都不同,則說明是一個新的子串。
完整代碼:
#include <cstdio> #include <cstring> #define LL long long using namespace std;const LL maxn = 200005, m1 = 999973, m2 = 1000000000 + 9;LL n, m, ans, tot;LL h[m1 + 5], h1[maxn], h2[maxn];char s[maxn];struct node {LL x, next; }a[maxn];void add(LL k1, LL k2) {a[tot].x = k2;a[tot].next = h[k1];h[k1] = tot++; }void hash(LL k1, LL k2) {for (LL i = h[k1]; ~i; i = a[i].next)if (a[i].x == k2)return;add(k1, k2);ans++; }LL ksm(LL a, LL b, LL mo) {LL ans = 1, base = a;while (b){if (b & 1)ans = (ans * base) % mo;base = (base * base) % mo;b >>= 1;}return ans % mo; }int main() {freopen("article.in","r",stdin);freopen("article.out","w",stdout);ans = tot = 0;memset(h, -1, sizeof h);scanf("%lld%lld", &n, &m);scanf("%s", s + 1);LL len = strlen(s + 1);h1[0] = h2[0] = 0;for (int i = 1; i <= len; i++){h1[i] = (h1[i - 1] * 131 + s[i]) % m1;h2[i] = (h2[i - 1] * 97 + s[i]) % m2;} for (LL i = 1; i <= len - m + 1; i++){LL k1 = ((h1[i + m -1] - h1[i - 1] * ksm(131, m, m1)) % m1 + m1) % m1;LL k2 = ((h2[i + m -1] - h2[i - 1] * ksm(97, m, m2)) % m2 + m2) % m2;hash(k1, k2);}printf("%lld\n", ans);return 0; } View Code?P.S.:
這題我使用的是哈希表的方法,要求m1一定要比較小,m2不能取太大,否則后來的運算中可能會出現溢出的情況。
我一般取m1=999973,m2=1e9+9
這里還有另外一種方法。
我們把每次hash得到的兩個數k1,k2,搞成一個pair。
然后把pair塞進一個vector中,排序并去重之,如此可以直接得到答案。
核心代碼:
h1[0] = h2[0] = 0;for (int i = 1; i <= len; i++){h1[i] = (h1[i - 1] * 131 + s[i]) % m1;h2[i] = (h2[i - 1] * 97 + s[i]) % m2;} for (LL i = 1; i <= len - m + 1; i++){LL k1 = ((h1[i + m -1] - h1[i - 1] * ksm(131, m, m1)) % m1 + m1) % m1;LL k2 = ((h2[i + m -1] - h2[i - 1] * ksm(97, m, m2)) % m2 + m2) % m2;v.push_back(make_pair(k1, k2));}sort(v.begin(), v.end());ans = unique(v.begin(), v.end()) - v.begin();printf("%lld\n", ans);?PSS:
之前寫的hash方法太慢,現在使用新的hash方法,大大提高了速度。(from O(nm) to O(n))
轉載于:https://www.cnblogs.com/yohanlong/p/7804699.html
總結
- 上一篇: struts2.0简单页面 (不带拦截
- 下一篇: ThinkPHP框架整合phpqrcod