Uva 6284 Hyperdrome

给一串长度为n的字符串,求一共有多少个子串重排后可为回文串。n为3*10^5,字符集为大写字母和小写字母。

我尝试再现一下思路...

首先,子串一定是一个串的某个前缀的后缀(涨姿势),所以子串共有n(n-1)/2个。

其次,一个回文串一定满足:1.当回文串长为奇数时,该串中只有一种字符数量为奇数,其他字符数量全部为偶数。2.当回文串长度为偶数时,所有出现的字符数量都为偶数。存储一个串的奇偶性可以利用位存储的方式存储其状态mask,字符集大小为52,所以需要52位来,longlongint即可满足。如某子串状态为mask,该子串=前缀2-前缀1.则有mask = mask2^mask1.

如果直接暴力的话,那么复杂度是为o(n^2*52)。显然不行。

事实上,我们无需与每一个前缀均异或运算,因为很多子串都是不满足条件的,这里就可以用状态压缩去除很多废情况。首先,用哈希存储一下每一个前缀的mask出现过多少次,对于当前的状态,只有之前的前缀状态相同,或者异或后有一位为1(状压枚举),才满足条件,这样的话就可以利用哈希去查找合法情况。这里有一个细节,就是如果当前状态就为回文串,也需要加到答案里面。因为字符集为52,已经超过int表示范围,所以在位运算的时候要将1强制转换为long long int型,否则会出错。

考虑周全以后1y,感觉也是对今天wa了一下午加一晚上的一种安慰吧...

#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;
#define N 1000007
#define MAXN 300005
#define LL long long

int head[N],nn;

typedef struct{
    LL num,key;
    int next;
}node;

node ele[N];
char inp[MAXN];
LL save[MAXN];

int ret(char a){
    if(a>='a'&&a<='z') return (a-'a');
    else{
        return (26+a-'A');
    }
}

void Insert(LL tmp){
     LL x;
     x=tmp%N;
     for(int i=head[x];~i;i=ele[i].next){
        if(ele[i].key==tmp) {
            ele[i].num++;
            return ;
        }
     }
        ele[nn].num=1;
        ele[nn].key=tmp;
        ele[nn].next=head[x];
        head[x]=nn++;
}

LL Search(LL tmp){
     LL x;
     x=tmp%N;
     for(int i=head[x];~i;i=ele[i].next){
        if(ele[i].key==tmp){
            return ele[i].num;
        }
     }
     return 0;
}

int main()
{
    int n;
    while(~scanf("%d",&n)){
        LL ans=0;
        nn=0;
        memset(head,-1,sizeof(head));
        scanf("%s",inp);
        LL tmp=0;
        for(int i=0;i<n;i++){
            tmp^=((LL)1<<ret(inp[i]));
            Insert(tmp);
            if(tmp==0) ans++;
            ans+=Search(tmp)-1;
            if(i%2==0){
                int cnt=0;
                for(int j=0;j<52;j++){
                    if(tmp&((LL)1<<j)) cnt++;
                    if(cnt>1) break;
                }
                if(cnt==1) ans++;
            }
            for(int j=0;j<52;j++){
                ans+=Search(tmp^((LL)1<<j));
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值