题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4426
WA:
1.k有没有 long long
2.回文串是取前一半
解题思路:
利用回文树获取本质不同的回文串的数量以及他们回文串前一半第一次出现的位置(起点终点位置)。
我们搞一个结构体:记录上面的信息,同时还需要一个变量计算hash值
对于每一次询问,我们O(N)预处理整个串的hash以及26的幂次方(后者总共只需预处理一次),然后O(1)计算每个回文串的hash值,sort对结构体按照hash大小排个序。
排好序后,然后开一个数组维护排序后回文串数量前缀和,二分查找k,看k落在哪个前缀和里面,就取哪个前缀和下标对应结构体的hash值
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#include<set>
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i<b;i++)
#define rof1(i,a,b) for (int i=a;i>=b;i--)
#define rof0(i,a,b) for (int i=a;i>b;i--)
#define pb push_back
#define fi first
#define se second
#define debug(x) printf("----Line %s----\n",#x)
#define pt(x,y) printf("%s = %d\n",#x,y)
#define INF 0x3f3f3f3f
#define df(x) ll x;scanf("%I64d",&x)
#define df2(x,y) ll x,y;scanf("%I64d %I64d",&x,&y)
#define mod 777777777
#define duozu(T) int T;scanf("%d",&T);while (T--)
const int N = 1e5+5;
const int maxn = 1e5+5;
const int ALP = 26;
char s[N];
struct node
{
int l,r;
ll hsh;
int cnt;
bool operator < (const node& a)const{
return hsh < a.hsh;
}
}pr[maxn];
ll pre[maxn],hsh[maxn],mi[maxn];
void prework()
{
mi[0] = 1;
mi[1] = 26;
for1(i,1,N-5) mi[i] = (mi[i-1]*26)%mod;
}
struct PAM{
int next[maxn][ALP];
int fail[maxn];
int len[maxn];
int s[maxn];
int last;
int cnt[maxn];
int n;
int p;
int newnode(int w){
for(int i=0;i<ALP;i++)
next[p][i] = 0;
len[p] = w;
cnt[p] = 0;
return p++;
}
void init(){
p = 0;
newnode(0);
newnode(-1);
last = 0;
n = 0;
s[n] = -1;
fail[0] = 1;
}
int get_fail(int x){
while(s[n-len[x]-1] != s[n]) x = fail[x];
return x;
}
void add(int c){
c -= 'a';
s[++n] = c;
int cur = get_fail(last);
if(!next[cur][c]){
int now = newnode(len[cur]+2);
fail[now] = next[get_fail(fail[cur])][c];
next[cur][c] = now;
pr[now].l = n-len[now]+1;
pr[now].r = pr[now].l + (len[now]+1)/2-1;
}
last = next[cur][c];
cnt[last]++;
}
void count(){
for(int i=p-1;i>=0;i--)
cnt[fail[i]] += cnt[i];
}
}pam;
int val[26];
int main()
{
//freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
//freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
int n,q;
ll k;
prework();
duozu(T){
scanf("%d %d",&n,&q);
scanf("%s",s);
pam.init();
for0(i,0,n) pam.add(s[i]);
pam.count();
for0(i,2,pam.p) pr[i].cnt = pam.cnt[i];
while (q--){
scanf("%lld",&k);
for0(i,0,26) scanf("%d",val+i);
hsh[1] = val[s[0]-'a'];
for0(i,1,n) hsh[i+1] = (hsh[i]*26 + val[s[i]-'a'])%mod;///哈希从1开始表示第几个到第几个
for0(i,2,pam.p){
int l = pr[i].l,r = pr[i].r;///第几个到第几个
pr[i].hsh = (hsh[r] - hsh[l-1]*mi[r-l+1]%mod + mod)%mod;
//printf("pr[i].hsh=%lld\n",pr[i].hsh);
}
sort(pr+2,pr+pam.p);
for0(i,2,pam.p) pre[i] = pre[i-1] + pr[i].cnt;
int pos = lower_bound(pre+2,pre+pam.p,k)-pre;
printf("%lld\n",pr[pos].hsh);
}
puts("");
}
return 0;
}
总结:牵扯到回文串数量的相关变量记得谨慎思考会不会爆int,或者干脆longlong一了百了。