第一次在考场上A的容斥题。
首先算仅d处不相同的字符串有几对就是求4-d处相同的字符串有几对。
记 d p [ i ] dp[i] dp[i]为仅有 i i i处相同的字符串有几对
假设我们要求仅有2处相同字符串对数,那么我们可以先枚举是哪两位相同,然后求出至少这两位相同的字符串对数,记为T。(这个过程可以通过map,字典树来实现(zz的我果断选择字典树))
然后我们可以发现仅有3处相同的字符串对被我们算了3遍( C 3 2 C^{2}_{3} C32),仅有4处相同的字符串对被我们算了6( C 4 2 C^{2}_{4} C42)遍,都要减掉。
于是 d p [ 2 ] = T − d p [ 3 ] ∗ 3 − d p [ 4 ] ∗ 6 dp[2]=T-dp[3]*3-dp[4]*6 dp[2]=T−dp[3]∗3−dp[4]∗6。
其他几个同理。
仅有0处相同的字符串的对数就是总对数减去 ( d p [ 1 ] + d p [ 2 ] + d p [ 3 ] + d p [ 4 ] ) (dp[1]+dp[2]+dp[3]+dp[4]) (dp[1]+dp[2]+dp[3]+dp[4])。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define N 50005
using namespace std;
char tmp[N][10];//tmp是原来的字符串
char S[10];
int ID[10];
struct Tree{//这里拿字典树统计,也可以拿map来
int son[N*4][260];
vector<int>way[N*4];
int cnt[N*4];
int tot;
void clear(){
tot=0;
way[0].clear();
memset(son[0],0,sizeof(son[0]));
cnt[0]=0;
}
void Add(char *A){
int now=0;
cnt[0]++;
for(int i=0;i<4;i++){
int x=A[i];
if(son[now][x]==0){
son[now][x]=++tot;
memset(son[tot],0,sizeof(son[tot]));
cnt[tot]=0;
way[tot].clear();
way[now].push_back(x);
}
now=son[now][x];
cnt[now]++;
}
}
long long ans;
void dfs(int now,int fa,int dep,int lim){
if(dep==lim){
ans+=1ll*cnt[now]*(cnt[now]-1);//统计有前lim个位置相同的字符串对数
return;
}
for(int i=0;i<(int)way[now].size();i++){
int nxt=son[now][way[now][i]];
dfs(nxt,now,dep+1,lim);
}
}
}ST;
bool mark[5555];
long long dp[10];
int n;
int num[10];
void Solve(int K){
for(int i=1;i<=4;i++)ID[i]=i;
memset(mark,0,sizeof(mark));
long long T=0;
do{
int res=0;
for(int i=1;i<=K;i++)num[i]=ID[i];
sort(num+1,num+K+1);
for(int i=1;i<=K;i++)res=res*10+num[i];//Hash判重
if(mark[res])continue;//上为枚举哪些位置相同
ST.clear();
mark[res]=true;
for(int i=1;i<=n;i++){
for(int j=0;j<4;j++)S[j]=tmp[i][ID[j+1]-1];//把要相同的位置放到最前面
ST.Add(S);//加入字典树
}
ST.ans=0;
ST.dfs(0,0,0,K);
T+=ST.ans;//统计答案
}while(next_permutation(ID+1,ID+5));
T/=2;//注意要除以二(不然(1,2)和(2,1)就会算两对)
if(K==4)dp[K]=T;//式子们
else if(K==3){
dp[K]=T-dp[4]*4;
}else if(K==2){
dp[K]=T-dp[3]*3-dp[4]*6;
}else if(K==1){
dp[K]=T-dp[2]*2-dp[3]*3-dp[4]*4;
}else dp[K]=T-dp[1]-dp[2]-dp[3]-dp[4];
}
int main(){
int d;
ST.tot=0;
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++)scanf("%s",tmp[i]);
for(int i=4;i>=0;i--)Solve(i);//一个一个算
printf("%lld\n",dp[4-d]);
return 0;
}