F - F. Palindrome Gym - 100676F

题意:给出一个字符串,其中有?的代表未填的字符,在?中可以填入任意小写英文字母,问有多少种填法可以使得字符串为回文字符串并且满足所给的m个要求:给出的两个位置的字符要相同。

题解:用并查集。先对字符串中位置对称的字符进行处理,再对每个要求中的两个字符进行处理。处理方式:

1.如果两个字符相同,则将他们则以任意一个点为根节点。

2.如果其中一个字符是'?',则一另一个字符为根节点。

3.如果不相同且都不为'?',则不存在回文串,输出0。

根节点确定的点只有1种固定填法,根节点为’?‘的有26种填法。

然后找出所有根节点为'?'的字符的个数n个,每一个?都可以填入任意的26个字母,有26^n中。记得取模。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;

const int mod = 1e9 + 7;
int t,n,m,flag,fa[50010];
char s[50010];

int Find(int x)
{
    if(x == fa[x]) return x;
    return fa[x] = Find(fa[x]);
}

void unite(int x,int y)
{
    if(s[x] == s[y]) fa[x] = y;
    else if(s[x] == '?') fa[x] = y;
    else if(s[y] == '?') fa[y] = x;
    else flag = 0;
}

int init()
{
    int cnt = 0;
    for(int i = 1;i <= n;i++) fa[i] = i;
    for(int i = 1;i <= (n + 1) / 2;i++) unite(i,n + 1 - i);
    for(int i = 1;i <= m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        x = Find(x),y = Find(y);
        unite(x,y);
    }
    for(int i = 1;i <= n;i++)
    {
        if(s[i] == '?' && fa[i] == i)
            cnt++;
    }
    return cnt;
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        flag = 1;
        scanf("%d%d%s",&n,&m,s + 1);
        int cont = init();
        long long ans = 1;
        if(!flag)
        {
            printf("0\n");
            continue;
        }
        for(int i = 1;i <= cont;i++)
            ans = ans * 26 % mod;
        printf("%I64d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Eric_chen_song_lin/article/details/81173019