[题目大意]
给你两个由小写字母和0、1组成的字符串。每一个小写字母都代表一个长度已知的01串,问能使这两个字符串相同的解有多少个。比如已知字母a的长度为10000,给出的两个字符串分别为a和1,则解一共有2^10000个。
[分析]
因为每个小写字母对应的01串的长度都已知,所以可以将每个小写字母对应的01串拆开,及a1a2…an,b1b2b3…bn的形式。因为两个字符串相同,所以它们对应位置的两个字符必然相同。我们把它们连一条边,最后就能形成数个联通块。我们只要统计出不含0、1,及不确定是0还是1的联通块的个数k,就可以计算出答案2^k。因为这道题目解的个数比较大,所以不要忘记高精度哦。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct bign{
int num[10000],len;
bign(int a=0){
memset(num,0,sizeof(num));
len=1;
num[len]=a;
}
bign operator = (const char *str){
len=strlen(str);
for(int i=1;i<=len;i++)num[i]=str[len-i]-'0';
return *this;
}
bign operator = (const int a){
char str[10000];
sprintf(str,"%d",a);
*this=str;
return *this;
}
bign operator *=(const int a){
int b=0;
for(int i=1;i<=len+1;i++){
num[i]=num[i]*a+b;
b=num[i]/10;
if(num[i]>=10){
num[i]%=10;
}
}
if(num[len+1])len++;
return *this;
}
void print(){
for(int i=len;i>=1;i--)printf("%d",num[i]);
}
};
struct point{
bool is_number;
int ler,inx,number;
point(int x=0,int y=0):ler(x),inx(y),is_number(false),number(0){}
bool operator != (const point &b)const{
return ((is_number!=b.is_number)||(!is_number&&!b.is_number&&(ler!=b.ler||inx!=b.inx))||(is_number
&&b.is_number&&number!=b.number));
}
bool operator == (const point &b)const{
return !((*this)!=b);
}
};
int t,n;
int l1,l2;
char s1[10010],s2[10010];
int num[10010];
bool vis[30][10010];
point six[30][10010];
point l[100100],r[100100];
void calm(){
printf("0\n");
}
bign power(int a,int b){
bign ans=1;
for(int i=1;i<=b;i++){
ans*=2;
}
return ans;
}
void init(){
memset(vis,0,sizeof vis);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&num[i]);
scanf("%d%s%d%s",&l1,s1,&l2,s2);
for(int i=0;i<=1;i++)six[0][i].ler=0,six[0][i].inx=i,six[0][i].is_number=true,six[0][i].number=i;
for(int i=1;i<=n;i++)for(int j=1;j<=num[i];j++)six[i][j]=point(i,j);
}
point find(point x){
point tmp=x,pre;
while(tmp!=six[tmp.ler][tmp.inx])tmp=six[tmp.ler][tmp.inx];
while(x!=tmp){
pre=six[x.ler][x.inx];
six[x.ler][x.inx]=tmp;
x=pre;
}
return tmp;
}
void work(){
int r1=0,r2=0;
for(int i=0;i<l1;i++){
if(s1[i]=='0'||s1[i]=='1')l[++r1].is_number=true,l[r1].number=s1[i]-'0',l[r1].ler=0,l[r1].inx=s1[i]-'0';
else {
for(int j=1;j<=num[s1[i]-'a'+1];j++)
l[r1+j].is_number=false,l[r1+j].ler=s1[i]-'a'+1,l[r1+j].inx=j;
r1+=num[s1[i]-'a'+1];
}
}
for(int i=0;i<l2;i++){
if(s2[i]=='0'||s2[i]=='1')r[++r2].is_number=true,r[r2].number=s2[i]-'0',r[r2].ler=0,r[r2].inx=s2[i]-'0';
else {
for(int j=1;j<=num[s2[i]-'a'+1];j++)
r[r2+j].is_number=false,r[r2+j].ler=s2[i]-'a'+1,r[r2+j].inx=j;
r2+=num[s2[i]-'a'+1];
}
}
if(r1!=r2){calm();return;}
for(int i=1;i<=r1;i++){
point rl=find(l[i]),rr=find(r[i]);
if(rl==rr)continue;
if(rl.is_number&&rr.is_number&&rl.number!=rr.number){calm();return;}
else if(!rl.is_number&&!rr.is_number)six[rr.ler][rr.inx]=rl;
else if(rl.is_number&&!rr.is_number){six[rr.ler][rr.inx].is_number=true;six[rr.ler][rr.inx].number=rl.number;}
else {six[rl.ler][rl.inx].is_number=true;six[rl.ler][rl.inx].number=rr.number;}
}
int tot=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=num[i];j++){
point tmp=find(six[i][j]);
if(!tmp.is_number&&!vis[tmp.ler][tmp.inx]){
vis[tmp.ler][tmp.inx]=true;
tot++;
}
}
power(2,tot).print();
printf("\n");
}
int main(){
freopen("row.in","r",stdin);
freopen("row.out","w",stdout);
scanf("%d",&t);
for(int i=1;i<=t;i++){
init();
work();
}
return 0;
}