CSAcademy Palindromic Concatenation 字符串哈希

题意:

题目链接
给出\(n\)个字符串,求有多少对\((i,j),i \neq j\)使得\(s_i\)\(s_j\)拼起来是回文串

分析:

\(s_i,s_j\)的长度分别为\(L_i, L_j\),一共有如下三种情况:

  • \(L_i=L_j\),那么有\(s_i\)等于\(s_j\)的反串,\(s_i,s_j\)构成回文串,注意\(s_i\)本身是回文串的情况,需要从答案中减去
  • \(L_i > L_j\),那么有\(s_i\)的某个后缀是回文串,并且\(s_j\)是剩余部分的反串
  • \(L_i < L_j\),分析方法同上

用两个map<hash, int>分别来记录正串和反串的\(hash\)
并且预处理所有字符串的前缀后缀的正串反串的\(hash\)值,这样就可以\(O(1)\)判断某个前缀后缀是否为回文串
第一种情况直接计数,对于后两种情况每次枚举较长串的回文前缀和后缀即可

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <map>
using namespace std;
#define REP(i, a, b) for(int i = a; i < b; i++)
#define PER(i, a, b) for(int i = b - 1; i >= a; i--)

typedef long long LL;

const int maxn = 100000 + 10;
const int MOD[2] = { 1000000007, 1000000009 };

int n;

struct Hash {
    int h[2];
    Hash(int h0 = 0, int h1 = 0) { h[0] = h0; h[1] = h1; }
    Hash(char c) { h[0] = c-'a'+1; h[1] = c-'a'+1; }
    bool operator < (const Hash& t) const {
        return h[0] < t.h[0] || (h[0] == t.h[0] && h[1] < t.h[1]);
    }
    Hash operator * (const Hash& t) const {
        return Hash(1LL * t.h[0] * h[0] % MOD[0], 1LL * t.h[1] * h[1] % MOD[1]);
    }
    Hash operator + (const Hash& t) const {
        return Hash((h[0] + t.h[0]) % MOD[0], (h[1] + t.h[1]) % MOD[1]);
    }
    bool operator == (const Hash& t) const {
        return h[0] == t.h[0] && h[1] == t.h[1];
    }
    void debug() { printf("{ %d, %d }\n", h[0], h[1]); }
};

Hash p(1, 1);
const Hash base(27, 27);

Hash addLeft(const Hash& a, char c) {
    return (Hash(c) * p) + a;
}

Hash addRight(const Hash& a, char c) {
    return (a * base) + Hash(c);
}

vector<string> s;
map<Hash, int> Normal, Reverse;
vector<Hash> preNormal[maxn], preReverse[maxn], sufNormal[maxn], sufReverse[maxn];

int main() {
    scanf("%d", &n); s.resize(n);
    REP(i, 0, n) {
        cin >> s[i];
        int l = s[i].length();

        p = Hash(1, 1);
        preNormal[i].emplace_back(0, 0);
        preReverse[i].emplace_back(0, 0);
        REP(j, 0, l) {
            preNormal[i].push_back(addRight(preNormal[i][j], s[i][j]));
            preReverse[i].push_back(addLeft(preReverse[i][j], s[i][j]));
            p = p * base;
        }

        p = Hash(1, 1);
        sufNormal[i].resize(l + 1);
        sufReverse[i].resize(l + 1);
        sufNormal[i][l] = sufReverse[i][l] = Hash(0, 0);
        PER(j, 0, l) {
            sufNormal[i][j] = addLeft(sufNormal[i][j+1], s[i][j]);
            sufReverse[i][j] = addRight(sufReverse[i][j+1], s[i][j]);
            p = p * base;
        }
        Normal[preNormal[i][l]]++;
        Reverse[preReverse[i][l]]++;
    }

    LL ans = 0;
    REP(i, 0, n) {
        int l = s[i].length();

        //case 1
        if(Reverse.count(preNormal[i][l]))
            ans += Reverse[preNormal[i][l]];
        if(preNormal[i][l] == preReverse[i][l]) ans--;

        //case 2
        PER(j, 1, l) if(sufNormal[i][j] == sufReverse[i][j]) {
            if(Reverse.count(preNormal[i][j]))
                ans += Reverse[preNormal[i][j]];
        }

        //case 3
        REP(j, 1, l) if(preNormal[i][j] == preReverse[i][j]) {
            if(Normal.count(sufReverse[i][j]))
                ans += Normal[sufReverse[i][j]];
        }
    }

    cout << ans << endl;

    return 0;
}
posted @ 2017-03-24 21:21 AOQNRMGYXLMV 阅读(...) 评论(...) 编辑 收藏