• [一本通学习笔记] 字符串哈希


    做字符串哈希我们通常要选定一个模数和一个基底。这样我们就可以快速处理出一个字符串的hash,并且可以做到log时间内求出一个子串的Hash。

    哈希表虽然好写并且跑得快但心里总有点发虚。

    #10034. 「一本通 2.1 例 2」图书管理

    题目很裸,注意getline(cin,...)的使用。

    #include <bits/stdc++.h>
    using namespace std;
    
    #define modulo 10000007
    #define base 131
    
    int buck[10000009];
    
    int getHash(string s) {
        int res = 0;
        for(int i=0;i<s.length();i++)
            res = ((res*base)+s[i])%modulo;
        return res;
    }
    
    int main() { ios::sync_with_stdio(false);
        int n;
        cin>>n;
        string tmp;
        getline(cin,tmp);
        for(int i=1;i<=n;i++) {
            getline(cin,tmp);
            if(tmp[0]=='a')
                buck[getHash(tmp.substr(4,tmp.length()-4))]=1;
            else
                cout<<(buck[getHash(tmp.substr(5,tmp.length()-5))]?"yes":"no")<<endl;
        }
    }

    #10035. 「一本通 2.1 练习 1」Power Strings

    这题有SA或者KMP的经典解法,当然没有hash简单方便了。

    暴力枚举循环节长度即可。

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    const ll modulo = 1000000007ll;
    const ll base = 131ll;
    ll qpow(ll p,ll q) {
        ll ret=1,bas=p;
        while(q) {
            if(q&1) ret*=bas;
            ret%=modulo;
            bas*=bas;
            bas%=modulo;
            q>>=1;
        }
        return ret;
    }
    ll h[1000005];
    string str;
    void prehash() {
        h[0]=str[0];
        for(ll i=1;i<str.length();i++)
            h[i]=(str[i]+h[i-1]*base)%modulo;
    }
    ll gethash(ll l,ll r) {
        if(l==0) return h[r];
        return (((h[r]-h[l-1]*qpow(base,r-l+1)%modulo))%modulo+modulo)%modulo;
    }
    
    int main() { ios::sync_with_stdio(false);
        while(cin>>str) {
            if(str[0]=='.') return 0;
            prehash();
            int n=str.length();
            for(int i=1;i<=n;i++) {
                if(n%i) continue;
                int flag = 1,
                    val = gethash(1-1,i-1);
                for(int j=i+1;j<=n;j+=i)
                    if(gethash(j-1,j+i-1-1)!=val) {
                        flag=0;
                        break;
                    }
                if(flag) {
                    cout<<n/i<<endl;
                    break;
                }
            }
        }
    }

    #10036. 「一本通 2.1 练习 2」Seek the Name, Seek the Fame

    看起来更像是KMP,当然暴力枚举hash判断也是轻松的。

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    const ll modulo = 1000000007;
    const ll base = 131;
    
    string str;
    ll h[1000005];
    
    ll qpow(ll p,ll q) {
        ll r = 1;
        for(; q; p*=p, p%=modulo, q>>=1) if(q&1) r*=p, r%=modulo;
        return r;
    }
    
    void prehash() {
        for(int i=0;i<=str.length();i++) h[i]=0;
        h[0] = str[0];
        for(int i=1;i<str.length();i++)
            h[i] = (str[i] + h[i-1]*base) % modulo;
    }
    
    ll gethash(int l,int r) { // Warning: index = 0..n-1
        if(l==0) return h[r];
        return ((h[r] - h[l-1]*qpow(base,r-l+1))%modulo + modulo)%modulo;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        while(cin>>str) {
            prehash();
            int n = str.length();
            for(int i=1;i<=n;i++)
                if(gethash(0,i-1) == gethash(n-i,n-1))
                    cout<<i<<" ";
            cout<<endl;
        }
    }

    #10038. 「一本通 2.1 练习 4」A Horrible Poem

    如果直接枚举区间长度的所有因子作为循环倍率,那么显然会TLE。

    考虑到循环次数一定是每个字母出现次数的倍数。所以循环次数一定同时还是是每个字母出现次数最小公倍数的因数。

    一开始脑抽写错了判断边界WA了一堆。后来发现自己傻逼,并得到了80分代码。

    #include <bits/stdc++.h>
    using namespace std;
    #define ll unsigned long long
    const ll base = 131;
    ll n,q,h[1000005],c[30][500005],pw[1000005];
    char str[500005];
    string s;
    ll gethash(int l,int r) {
        return ((h[r]-(l?h[l-1]:0)*pw[r-l+1]));
    }
    ll gcd(ll x,ll y) {
        return (!y)?x:gcd(y,x%y);
    }
    
    int main() {
        pw[0]=1;
        for(int i=1;i<=1000000;i++) pw[i]=pw[i-1]*base;
        cin>>n>>s>>q;
        h[0]=s[0];
        c[s[0]-'a'][0]=1;
        for(int i=1;i<n;i++)
            for(int j=0;j<26;j++)
                c[j][i]=c[j][i-1]+((s[i]==j+'a')?1:0);
        for(int i=1;i<n;i++)
            h[i]=(h[i-1]*base+(ll)s[i]);
        for(int i=1;i<=q;i++) {
            ll l,r;
            cin>>l>>r; --l,--r;
            // siz * tim = len
            // siz | len
            // tim | gcd
            int len = r-l+1;
            ll g=0;
            for(int i=0;i<26;i++)
                g=gcd(c[i][r]-(l?c[i][l-1]:0),g);
            //cout<<"g "<<g<<endl;
            int flag = 0;
            for(int t=1;t*t<=g;t++) {
                int tim = g/t;
                if(g%t) continue;
                if(len%tim) continue;
                int siz=len/tim;
                if(gethash(l,r-siz)==gethash(l+siz,r)) {
                    printf("%d
    ",siz); flag=1;
                    break;
                }
            }
            if(flag) continue;
            for(int tim=sqrt(g);tim>=1;tim--) {
                int siz=len/tim;
                if(len%tim) continue;
                if(gethash(l,r-siz)==gethash(l+siz,r)) {
                    printf("%d
    ",siz); flag = 1;
                    break;
                }
            }
            if(flag) continue;
            cout<<"error"<<endl;
        }
    }

    在添加了快读并手工开方后得到了86分代码

    #include <bits/stdc++.h>
    using namespace std;
    #define ll unsigned long long
    const ll base = 131;
    ll n,q,h[1000005],c[30][500005],pw[1000005],sq[500005];
    char str[500005];
    string s;
    inline int read() {
        int x=0,f=1; char c=getchar();
        while(c<'0'||c>'9') { if(c=='-') f=-1; c=getchar(); }
        while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); }
        return x*f;
    }
    ll gethash(int l,int r) {
        return ((h[r]-(l?h[l-1]:0)*pw[r-l+1]));
    }
    ll gcd(ll x,ll y) {
        return (!y)?x:gcd(y,x%y);
    }
    int main() {
        pw[0]=1;
        for(int i=1;i<=1000000;i++) pw[i]=pw[i-1]*base;
        int _sqt = 1;
        for(int i=1;i<=500000;i++)  {
            if(_sqt*_sqt >= i) sq[i]=_sqt;
            else sq[i]=++_sqt;
        }
        n=read();
        gets(str);
        s=str;
        q=read();
        h[0]=s[0];
        c[s[0]-'a'][0]=1;
        for(int i=1;i<n;i++)
            for(int j=0;j<26;j++)
                c[j][i]=c[j][i-1]+((s[i]==j+'a')?1:0);
        for(int i=1;i<n;i++)
            h[i]=(h[i-1]*base+(ll)s[i]);
        for(int i=1;i<=q;i++) {
            ll l,r;
            l=read(); r=read(); --l,--r;
            // siz * tim = len
            // siz | len
            // tim | gcd
            int len = r-l+1;
            ll g=0;
            for(int i=0;i<26;i++)
                g=gcd(c[i][r]-(l?c[i][l-1]:0),g);
            //cout<<"g "<<g<<endl;
            int flag = 0;
            for(int t=1;t*t<=g;t++) {
                int tim = g/t;
                if(g%t) continue;
                if(len%tim) continue;
                int siz=len/tim;
                if(gethash(l,r-siz)==gethash(l+siz,r)) {
                    printf("%d
    ",siz); flag=1;
                    break;
                }
            }
            if(flag) continue;
            for(int tim=sq[g];tim>=1;tim--) {
                int siz=len/tim;
                if(len%tim) continue;
                if(gethash(l,r-siz)==gethash(l+siz,r)) {
                    printf("%d
    ",siz); flag = 1;
                    break;
                }
            }
            if(flag) continue;
        }
    }

    显然我们需要一些诡异的优化

    设最短的循环节长度为siz,那么原串长度len = siz*tim

    对tim,我们将它的质因子分解。

    我们每次用质因数i去试除len,如果除去以后仍然是循环节,就说明i是k的一个因子,将他除去。得到的结果就是len。

    #include <bits/stdc++.h>
    using namespace std;
    #define ll unsigned long long
    const ll base = 131;
    ll n, q, h[1000005], c[30][500005], pw[1000005], sq[500005], prime[500005], tot, mp[500005];
    char str[500005];
    string s;
    inline ll read() {
        ll x = 0, f = 1;
        char c = getchar();
        while (c < '0' || c > '9') {
            if (c == '-')
                f = -1;
            c = getchar();
        }
        while (c >= '0' && c <= '9') {
            x = x * 10 + c - '0';
            c = getchar();
        }
        return x * f;
    }
    ll gethash(ll l, ll r) { return ((h[r] - (l ? h[l - 1] : 0) * pw[r - l + 1])); }
    bool check(ll a, ll b, ll l) {
        return h[b] - h[a + l - 1] * pw[b - a - l + 1] ==
               h[a + ((b - a + 1) / l - 1) * l - 1] - (a ? h[a - 1] : 0) * pw[b - a - l + 1];
    }
    int main() {
        pw[0] = 1;
        for (ll i = 1; i <= 1000000; i++) pw[i] = pw[i - 1] * base;
        n = read();
        gets(str);
        s = str;
        q = read();
        for (ll i = 2; i <= n; i++) {
            if (mp[i] == 0) {
                prime[++tot] = i;
                mp[i] = i;
            }
            for (ll j = 1; j <= tot; j++) {
                if (prime[j] > mp[i] || prime[j] * i > n)
                    break;
                mp[prime[j] * i] = prime[j];
            }
        }
        h[0] = s[0];
        for (ll i = 1; i < n; i++) h[i] = (h[i - 1] * base + (ll)s[i]);
        for (ll i = 1; i <= q; i++) {
            ll l, r;
            l = read();
            r = read();
            --l, --r;
            ll len = r - l + 1;
            ll tmp = len, ans = len;
            while (tmp > 1) {
                ll t = mp[tmp];  // Min prime divisor
                while (tmp % t == 0 && check(l, r, ans / mp[tmp])) tmp /= t, ans /= t;
                while (tmp % t == 0) tmp /= t;
            }
            printf("%d
    ", ans);
        }
    }

    #10041. 「一本通 2.1 练习 7」门票

    #include <bits/stdc++.h>
    using namespace std;
    
    #define modulo 100000007
    bool u[modulo];
    
    #define ll long long
    
    int main() {
        ll A,B,C;
        cin>>A>>B>>C;
        ll x=1; u[1]=1;
        for(int i=1;i<=2000000;i++) {
            x=(A*x+x%B)%C;
            if(u[x%modulo]) {
                cout<<i<<endl;
                return 0;
            }
            u[x%modulo]++;
        }
        cout<<-1<<endl;
        return 0;
    }

    #10042. 「一本通 2.1 练习 8」收集雪花

    理论上这种题是不敢用哈希表做的。

    当然还是比离散化好写一点而且快很多的

    #include <bits/stdc++.h>
    using namespace std;
    #define modulo 100000007
    bool u[modulo];
    int ans,pin,n,a[1000005];
    
    int main() {
        ios::sync_with_stdio(false);
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        pin=1;
        for(int i=1;i<=n;i++) {
            while(u[a[i]%modulo])
                u[(a[pin++])%modulo]=0;
            u[a[i]%modulo]=1;
            ans=max(ans,i-pin+1);
        }
        cout<<ans<<endl;
    }
  • 相关阅读:
    mac下进行配置android真机调试环境
    给技术人上的管理课:控制和计划
    PNG 文件结构
    BMP文件结构
    开发环境FAQ
    VS2008通过 map 和 cod 文件定位崩溃代码行
    【Demo 0009】Android 组件(BroadcastReceiver)
    【Demo 0007】Android 信使(Intent)
    【Demo 0006】Android 组件(Activity)
    使用Topshelf 5步创建Windows 服务
  • 原文地址:https://www.cnblogs.com/mollnn/p/11604392.html
Copyright © 2020-2023  润新知