【2019徐州网络赛:G】Colorful String(回文树+二进制统计回文串内不同字母数技巧)

提交地址:https://nanti.jisuanke.com/t/41389

题目:


给出一个字符串,求字符串中所有回文串的不同字母数之和

解题思路:


建立回文树,nxt[cur][c]=now,表示在节点cur表示的最长回文子串左右两边加上字符'a'+c构成了新的最长回文子串,对应节点编号为now。

在统计每个最长回文子串内的不同字母数时,参照上图的回文树,从0/1节点向下走一条路径,比如0->6,那么以s[6-1]为结尾的最长回文子串的不同字母数为1,如0->6->7,那么以s[7-1]为结尾的最长回文子串的不同字母数为2。一个最长回文子串中的最大字母数为26,可以用二进制来统计。

具体的统计方法(在建树时完成统计):dif[now] = dif[cur]|(1<<c) 式子中的变量和nxt[cur][c]=now中的变量相对应,最终对于节点i表示的最长回文子串的不同字母数,就可以通过dif[i]中的1的个数得知。

小技巧:求int型的数 对应的二进制中1的个数,可直接调用内置函数__builtin_popcount(x)得到

ac代码:


#include <bits/stdc++.h>
using namespace std;
const int maxn = 300005;
typedef long long ll;
int fail[maxn], cnt[maxn], num[maxn], len[maxn], nxt[maxn][26], s[maxn];
int last, n, p;
char ss[maxn];
int dif[maxn]={0};
int newnode(int l) //l是节点对应的最长回文子串的长度
{
    for(int i = 0; i < 26; i++)
        nxt[p][i] = 0;
    len[p] = l;
    cnt[p] = 0;
    num[p] = 0;
    return p++;
}
void init()
{
    p = 0, last = 0, n = 0, s[0] = -1;
    newnode(0); // p=0的初始化
    newnode(-1); // p = 1的初始化,之后 p = 2
    fail[0] = 1;
    fail[1] = 0;
}
int get_fail(int x)
{
    while(s[n - len[x] - 1] != s[n])
        x = fail[x];
    return x;
}
void insert(int c) // c = ch - 'a'
{
    s[++n] = c;
    int cur = get_fail(last);
    if(!nxt[cur][c])
    {
        int now = newnode(len[cur] + 2); //now表示当前节点编号
        fail[now] = nxt[get_fail(fail[cur])][c];//此句和下面一句顺序一定不能倒!!
        //因为nxt初始化为0,若当前节点表示的回文串是它自己,那么它的fail应该指向0,此时cur=1,get_fail(fail[cur])=1
        nxt[cur][c] = now;
        num[now] = num[fail[now]] + 1; //1->n所能形成的回文串数目+1
        dif[now] = dif[cur] | (1 << c);
    }
    last = nxt[cur][c];//当前节点编号
    cnt[last]++;//以last为结尾的最长回文子串对应的数目+1
}
void count()
{
    for(int i = p - 1; i >= 0; i--) //从后往前更新
        cnt[fail[i]] += cnt[i];
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    init();
    scanf("%s", ss+1);//为了避免MLE,也可以边读字符边insert
    int lenn = strlen(ss+1);
    for(int i = 1; i <= lenn; i++)
        insert(ss[i] - 'a');
    count();
    ll ans = 0;
    for(int i = 2; i < p; i++)
        ans += 1LL * cnt[i] * __builtin_popcount(dif[i]);
    printf("%lld\n", ans);
    return 0;
}
发布了299 篇原创文章 · 获赞 81 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/Cassie_zkq/article/details/100606270