Gym-100676F Palindrome
原题连接:https://odzkskevi.qnssl.com/1110bec98ca57b5ce6aec79b210d2849?v=1491063604
题意:
多组输入,每一次输入一个n(字符串的长度),一个m(下面m条关系),一个字符串s,m条关系(要求x, y位的字符相等)。
字符串中有一个或者多个'?' ,'?'可以随机填26个小写英文字母。问你这样的字符串可以有多少种填写方法。
解题思路:
这一道题,用并查集的方法可以很快的解决。
因为第i个字符是一定与第n-1-i个字符相同的(i从0开始),加上m条要求之后,就可以获得多个集合,每一个集合都是要求相同的字符。
选择父亲的时候有讲究,尽量选择是字符作为祖先,'?' 插入到祖父的下面。
然后就是便利处理一下,把可以确定的 '?' 转换成字母。(只有全部是 '?' 的集合不能转换成字母)
最后就是遍历一下在字符串中 '?' 的集合的个数,答案就出来了。
****************************************************************************************************
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <string> #include <vector> #include <map> #include <set> #include <queue> #include <sstream> #include <algorithm> using namespace std; #define pb push_back #define mp make_pair #define mset(a, b) memset((a), (b), sizeof(a)) //#define LOCAL typedef long long LL; const int inf = 0x3f3f3f3f; const int maxn = 50000+10; const int mod = 1e9+7; char s[maxn]; int father[maxn]; int vis[maxn]; int findfa(int x) { return father[x]==-1? x : father[x] = findfa(father[x]); } void uni(int x, int y) { int fa1 = findfa(x); int fa2 = findfa(y); if(fa1!=fa2){ if(s[fa1]=='?') father[fa1] = fa2; else father[fa2] = fa1; } } int main() { #ifdef LOCAL freopen("input.txt" , "r", stdin); #endif // LOCAL int T; cin >> T; while(T--){ int n, m, x, y, flag = 1, num=0; mset(father, -1); mset(vis, 0); scanf("%d%d%s",&n,&m, s); for(int i=0;i<n;i++) uni(i, n-1-i); for(int i = 0;i<m;i++){ scanf("%d%d", &x, &y); uni(--x, --y); } for(int i=0;i<n;i++){ int x = findfa(i); if(s[i] == '?' && s[x] != '?') s[i] = s[x]; if(s[i] !='?' && s[x] !='?' && s[i]!=s[x]) {flag=0;break;}//如果字母和字母不对应,直接错误。 } if(!flag) printf("0\n"); else{ for(int i=0;i<n;i++){ if(s[i]=='?' && father[i] == -1){ num++; } } LL ans = 1LL; for(int i =0 ;i<num;i++) ans = (1LL*ans*26 )%mod; printf("%lld\n", ans); } } return 0; }
****************************************************************************************************