字符Hash初步

兔子與兔子

很久很久以前,森林裏住着一羣兔子。
有一天,兔子們想要研究自己的 DNA 序列。
我們首先選取一個好長好長的 DNA 序列(小兔子是外星生物,DNA 序列可能包含 26 個小寫英文字母)。
然後我們每次選擇兩個區間,詢問如果用兩個區間裏的 DNA 序列分別生產出來兩隻兔子,這兩個兔子是否一模一樣。
注意兩個兔子一模一樣只可能是他們的 DNA 序列一模一樣。

輸入格式
第一行輸入一個 DNA 字符串 S。
第二行一個數字 m,表示 m 次詢問。
接下來 m 行,每行四個數字 l1,r1,l2,r2,分別表示此次詢問的兩個區間,注意字符串的位置從1開始編號。

輸出格式
對於每次詢問,輸出一行表示結果。
如果兩隻兔子完全相同輸出 Yes,否則輸出 No(注意大小寫)。

數據範圍
1≤length(S),m≤1000000
輸入樣例:
aabbaabb
3
1 3 5 7
1 3 6 8
1 2 1 2
輸出樣例:
Yes
No
Yes


我們都知道Hash的思想就是把字符轉化爲便於比較的數字,
在Hash中取底的話,有兩個經驗值 131, 1331,這兩個的重複率更低。
還有一個值得注意的就是,我們可以用unsigned long long 來取模,這樣可以避免許多操作。

這就是一道hash板子題,不多說了,直接上代碼。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef unsigned long long ULL;
const int N = 1e6 + 10;
const ULL base = 131;

ULL h[N], p[N];//h放的是hash值,p中是base的i次方,在得到某一串的字符的hash值中有用。
char s[N];
ULL get(int l, int r) {
    return h[r] - h[l - 1] * p[r - l + 1];
}
int main() {
    p[0] = 1;
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    for(int i = 1; i <= n; i++) {
        h[i] = h[i - 1] * base + s[i] - 'a' + 1;
        p[i] = p[i - 1] * base;
    }
    int t;
    scanf("%d", &t);
    while(t--) {
        int l1, r1, l2, r2;
        scanf("%d %d %d %d", &l1, &r1, &l2, &r2);
        ULL a = get(l1, r1);
        ULL b = get(l2, r2);
        printf("%s\n", a == b ? "Yes" : "No");
    }
    return 0;
}


雪花雪花雪花

有N片雪花,每片雪花由六個角組成,每個角都有長度。
第i片雪花六個角的長度從某個角開始順時針依次記爲ai,1,ai,2,…,ai,6。
因爲雪花的形狀是封閉的環形,所以從任何一個角開始順時針或逆時針往後記錄長度,得到的六元組都代表形狀相同的雪花。
例如ai,1,ai,2,…,ai,6和ai,2,ai,3,…,ai,6,ai,1就是形狀相同的雪花。
ai,1,ai,2,…,ai,6和ai,6,ai,5,…,ai,1也是形狀相同的雪花。
我們稱兩片雪花形狀相同,當且僅當它們各自從某一角開始順時針或逆時針記錄長度,能得到兩個相同的六元組。
求這N片雪花中是否存在兩片形狀相同的雪花。

輸入格式
第一行輸入一個整數N,代表雪花的數量。
接下來N行,每行描述一片雪花。
每行包含6個整數,分別代表雪花的六個角的長度(這六個數即爲從雪花的隨機一個角順時針或逆時針記錄長度得到)。
同行數值之間,用空格隔開。

輸出格式
如果不存在兩片形狀相同的雪花,則輸出:
No two snowflakes are alike.
如果存在兩片形狀相同的雪花,則輸出:
Twin snowflakes found.

數據範圍
1≤n≤100000,
0≤ai,j<10000000
輸入樣例:
2
1 2 3 4 5 6
4 3 2 1 6 5
輸出樣例:
Twin snowflakes found.

這道hash與上面又有點不一樣,但是意思還是很好理解的,直接上代碼。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
typedef unsigned long long ULL;
map<ULL, int> m;
int a[10];
ULL get_hash()
{
    ULL sum = 0, mul = 1;
    for (int i = 1; i <= 6; i++) {
        sum = sum + a[i];
        mul = mul * a[i];
    }
    return sum + mul;
}
int main() {
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        for(int j = 1; j <= 6; j++)
            scanf("%d", &a[j]);
        ULL k = get_hash();
        if(m.count(k)) {
            puts("Twin snowflakes found.");
            return 0;
        }
        m[k]++;
    }
    puts("No two snowflakes are alike.");
    return 0;
}


迴文子串的最大長度

如果一個字符串正着讀和倒着讀是一樣的,則稱它是迴文的。
給定一個長度爲N的字符串S,求他的最長迴文子串的長度是多少。

輸入格式
輸入將包含最多30個測試用例,每個測試用例佔一行,以最多1000000個小寫字符的形式給出。
輸入以一個以字符串“END”(不包括引號)開頭的行表示輸入終止。

輸出格式
對於輸入中的每個測試用例,輸出測試用例編號和最大回文子串的長度(參考樣例格式)。
每個輸出佔一行。

輸入樣例:
abcbabcbabcba
abacacbaaaab
END
輸出樣例:
Case 1: 13
Case 2: 6

這道題目還是有點意思的,用了二分還有hash來找最迴文串。
爲了避免迴文串是偶數的討論,在每個字符間都插入了一個非字母符號來減小代碼討論量,所以我們只有二分迴文長度的一半就行,然後通過hash值比對,確定迴文串的長度。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef unsigned long long ULL;
const int N = 2e6 + 10, base = 131;
char s[N];
ULL hl[N], hr[N], p[N];
ULL hash1(int l, int r) {
    return hl[r] - hl[l - 1] * p[r - l + 1];
}
ULL hash2(int l, int r) {
    return hr[r] - hr[l - 1] * p[r - l + 1];
}
int main() {
    int t = 1;
    while(scanf("%s", s + 1) && strcmp(s + 1, "END")) {
        int n = strlen(s + 1);
        for(int i = 2 * n; i >= 1; i -= 2) {
            s[i] = s[i / 2];
            s[i - 1] = 'z' + 1;
        }
        p[0] = 1;
        n *= 2;
        for(int i = 1, j = n; i <= n; i++, j--) {
            hl[i] = hl[i - 1] * base + s[i] - 'a' + 1;
            hr[i] = hr[i - 1] * base + s[j] - 'a' + 1;
            p[i] = p[i - 1] * base;
        }
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            int l = 0, r = min(i - 1, n - i);
            while(l < r) {
                int mid = (l + r  + 1) >> 1;
                if(hash1(i - mid, i - 1) != hash2(n - (i + mid) + 1, n - (i + 1) + 1))  r = mid - 1;
                else    l = mid;
            }
            if(s[i + l] <= 'z') ans = max(ans, l + 1);//這一步要注意,我們插入的字符可能在最左右端,這個時候我們要特殊考慮。
            else    ans = max(ans, l);
        }
        printf("Case %d: %d\n", t++, ans);
    }
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章