原题链接
题目大意:给定n个字符串与q个询问,每个字符串均按编号顺序(1~n)输入。对于每个询问均有字符串a、b(记a的编号为l,b的编号为r),需要输出 [l , r] 间拥有最大回文数的编号。
本蒟蒻一开始以为用线段树查询区间最大值就能过,结果样例2就给T飞了。观察发现本题并没有在后续修改字符串,完全可以使用ST表查询O(1)解决。
解题思路
1.计算子串回文数
cbbaca 的子回文串有 c b b a c a bb aca,很容易联想到区间DP,只需要小小的 魔改一下。
bool ok[32][32];//ok[i][j]表示i~j是否为回文
for (int i = 1; i <= 30; i++)ok[i][i] = 1;
int calc(char s[])
{
int len = strlen(s + 1), cnt = len;
for (int i = 1; i < len; i++)//先处理长度为2的回文,即两个相同的字符为回文
cnt += ok[i][i + 1] = (s[i] == s[i + 1]);
for (int k = 2; k < len; k++)
for (int i = len - k; i >= 1; i--)
{
int j = i + k;
cnt += ok[i][j] = (ok[i + 1][j - 1] && s[i] == s[j]);
}
return cnt;
}
2.存储字符串
那肯定是用map啦
但是使用map<string,int>存储并不高效,因为string内部的比较需要较长时间,在样例6就给T了…
于是很自然就想到了 可以改用字符串的哈希值,绕过string的比较!使用后发现快了很多。
typedef unsigned long long ull;
map<ull, int> ma;
ull geth(char s[])
{
ull hash = 0;
for (int i = 1; s[i]; i++)
hash = hash * 131 + s[i];
return hash;
}
3.ST表
不多说直接套模板,st数组存字符串编号,cnt存子串回文数
int cnt[N], st[N][32], lg[N + 5] = { -1 };
for (int i = 1; i <= N; i++)lg[i] = lg[i / 2] + 1;//预处理log2
void initST(int n)
{
for(int i = 1; i <= n; i++) st[i][0] = i;
int k = lg[n];
for (int j = 1; j <= k; j++)
for (int i = 1; i + (1 << (j - 1)) <= n; i++)
if (cnt[st[i][j - 1]] >= cnt[st[i + (1 << (j - 1))][j - 1]])
st[i][j] = st[i][j - 1];
else
st[i][j] = st[i + (1 << (j - 1))][j - 1];
}
void solve()
{
char s1[35], s2[35];
scanf("%s%s", s1 + 1, s2 + 1);
int l = ma[geth(s1)], r = ma[geth(s2)];
if (r < l)swap(l, r);//注意r有可能小于l
int k = lg[r - l + 1];
if (cnt[st[l][k]] >= cnt[st[r - (1 << k) + 1][k]])
printf("%d\n", st[l][k]);
else
printf("%d\n", st[r - (1 << k) + 1][k]);
}
完整代码(无注释)
#include <iostream>
#include <algorithm>
#include <string.h>
#include <map>
using namespace std;
typedef unsigned long long ull;
const int N = 1e4 + 5;
map<ull, int> ma;
int cnt[N], st[N][32], lg[N + 5] = { -1 };
bool ok[32][32];
int calc(char s[])
{
int len = strlen(s + 1), cnt = len;
for (int i = 1; i < len; i++)
cnt += ok[i][i + 1] = (s[i] == s[i + 1]);
for (int k = 2; k < len; k++)
for (int i = len - k; i >= 1; i--)
{
int j = i + k;
cnt += ok[i][j] = (ok[i + 1][j - 1] && s[i] == s[j]);
}
return cnt;
}
ull geth(char s[])
{
ull hash = 0;
for (int i = 1; s[i]; i++)
hash = hash * 131 + s[i];
return hash;
}
void initST(int n)
{
for(int i = 1; i <= n; i++) st[i][0] = i;
int k = lg[n];
for (int j = 1; j <= k; j++)
for (int i = 1; i + (1 << (j - 1)) <= n; i++)
if (cnt[st[i][j - 1]] >= cnt[st[i + (1 << (j - 1))][j - 1]])
st[i][j] = st[i][j - 1];
else
st[i][j] = st[i + (1 << (j - 1))][j - 1];
}
void solve()
{
char s1[35], s2[35];
scanf("%s%s", s1 + 1, s2 + 1);
int l = ma[geth(s1)], r = ma[geth(s2)];
if (r < l)swap(l, r);
int k = lg[r - l + 1];
if (cnt[st[l][k]] >= cnt[st[r - (1 << k) + 1][k]])
printf("%d\n", st[l][k]);
else
printf("%d\n", st[r - (1 << k) + 1][k]);
}
int main()
{
for (int i = 1; i <= N; i++)lg[i] = lg[i / 2] + 1;
for (int i = 1; i <= 30; i++)ok[i][i] = 1;
int t;
scanf("%d", &t);
while (t--)
{
memset(st, 0, sizeof st);
ma.clear();
int n, q;
char s[35];
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++)
{
scanf("%s", s + 1);
ma[geth(s)] = i, cnt[i] = calc(s);
}
initST(n);
while (q--)solve();
}
return 0;
}
蒟蒻的第一篇题解,希望各位dalao手下留情,有不足多多指出(●ˇ∀ˇ●)