题目描述
输入输出格式
输入格式:
本题包含多组数据。 第一行:一个整数T,表示数据的个数。 对于每组数据: 第一行:两个整数,N和K(含义如题目表述)。 接下来N行:每行一个字符串。
输出格式:
1 2 1 a? ?b
输入输出样例
输入样例#1:
50
输出样例#1:
对于30%的数据,T ≤ 5,M ≤ 5,字符串长度≤ 20; 对于70%的数据,T ≤ 5,M ≤ 13,字符串长度≤ 30; 对于100%的数据,T ≤ 5,M ≤ 15,字符串长度≤ 50。
真是玄学题
经历了从50分到70分再到AC
一开始看到觉得是带通配符的AC自动机状压DP,一看要求长度都一样,好像直接DP就行呀?
然后写了最朴素的DP,dp[i][j]表示转移到第i个字符,目前能匹配上的状态为j的方案数,然后枚举下一位是什么,与所有字符串进行比较转移即可
这样的复杂度是 $ O(26TLn*2^n) $ ,能有50分
然后可以发现与所有字符串比较是很愚蠢的,进行了很多重复计算,我们可以预处理出每一位所有字符串匹配某个字符的结果,压在一个int里,转移时直接按位与即可
这样复杂度是 $ O(26TL*2^n) $ ,能有70分
然后你把它数字带进去发现,它乘出来是26*5*50*2^15约等于2e8,这是什么意思?这意思就是稍微卡一卡就能过了。
DP怎么稍微卡一卡呢?你想DP倒过来就是记忆化搜索,搜索可以剪枝啊,于是我们尝试剪一剪枝。
怎么剪枝呢?加上如果在遍历到某个状态dp[i][j]时,如果dp[i][j]==0就不转移
事实上这个效果好得出奇,速度快了10倍都不止,至此获得100分
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <algorithm> 5 #include <string> 6 #include <cstring> 7 #include <cmath> 8 #include <map> 9 #include <stack> 10 #include <set> 11 #include <vector> 12 #include <queue> 13 #include <time.h> 14 #define eps 1e-7 15 #define INF 0x3f3f3f3f 16 #define MOD 1000003 17 #define rep0(j,n) for(int j=0;j<n;++j) 18 #define rep1(j,n) for(int j=1;j<=n;++j) 19 #define pb push_back 20 #define set0(n) memset(n,0,sizeof(n)) 21 #define ll long long 22 #define ull unsigned long long 23 #define iter(i,v) for(edge *i=head[v];i;i=i->nxt) 24 #define max(a,b) (a>b?a:b) 25 #define min(a,b) (a<b?a:b) 26 #define print_runtime printf("Running time:%.3lfs ",double(clock())/1000.0) 27 #define TO(j) printf(#j": %d ",j); 28 //#define OJ 29 using namespace std; 30 const int MAXINT = 100010; 31 const int MAXNODE = 100010; 32 const int MAXEDGE = 2*MAXNODE; 33 char BUF,*buf; 34 int read(){ 35 char c=getchar();int f=1,x=0; 36 while(!isdigit(c)){if(c=='-') f=-1;c=getchar();} 37 while(isdigit(c)){x=x*10+c-'0';c=getchar();} 38 return f*x; 39 } 40 char get_ch(){ 41 char c=getchar(); 42 while(!isalpha(c)) c=getchar(); 43 return c; 44 } 45 //------------------- Head Files ----------------------// 46 47 int dp[51][1<<15],n,k,l,cnt[1<<15]; 48 unsigned int val[51][26]; 49 char s[15][60]; 50 void get_input(); 51 void work(); 52 int main() { 53 int T=read(); 54 rep1(i,(1<<15)-1){ 55 cnt[i]=cnt[i-(i&-i)]+1; 56 } 57 while(T--){ 58 get_input(); 59 work(); 60 } 61 return 0; 62 } 63 void work(){ 64 int mx = (1<<n); 65 memset(val,0xffff,sizeof(val)); 66 rep1(i,l){ 67 rep0(j,26){ 68 rep0(k,n){ 69 if(s[k][i]!='?'&&s[k][i]!='a'+j) val[i][j]&=(~(1<<k)); 70 } 71 } 72 } 73 dp[0][(1<<n)-1] = 1; 74 rep0(i,l){ 75 rep0(j,mx){ 76 if(dp[i][j]==0) continue; 77 rep0(k,26){ 78 int t = j; 79 t&=val[i+1][k]; 80 dp[i+1][t]+=dp[i][j]; 81 dp[i+1][t]%=MOD; 82 } 83 } 84 } 85 ll ans = 0; 86 rep0(i,mx) if(cnt[i]==k) ans+=dp[l][i]; 87 printf("%lld ",ans%MOD); 88 } 89 void get_input(){ 90 memset(dp,0,sizeof(dp)); 91 n=read();k=read(); 92 rep0(i,n) scanf("%s",s[i]+1); 93 l=strlen(s[0]+1); 94 }