Tn3复杂度能过?
搬题解
逆向思维. 设d[i,j,c]为真当且仅当[i,j)的子串可以合成字母c. 递推时可枚举分裂位置k: 对于某个规则A1A2A3, 如果d[i,k,A2]与d[k,j,A3]均为真, 则d[i,j,A1]为真. 即:
d[i,j,A1] = d[i,j,A1]OR (A[i,k,A2] AND A[k,j,A3])
状态有cn2个, 转移有c2n个, 总O(c3n3)。
g[i,j]为[I,j)的子串最少可以合成几个S,直接动态规划即可, 时间复杂度为O(n2).
优化:最多只有26个字母, 因此可以用位运算加速
e[A2,A3]表示可以分裂得到A2A3的字符集
d’[I,j]表示所有c对应的d[i,j,c]所组成的二进制数
时间复杂度降为O(c2n3).
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline int read(char *s){
char c=nc(); int len=0;
for (;!(c>='A' && c<='Z');c=nc());
for (;c>='A' && c<='Z';s[++len]=c,c=nc()); s[++len]=0; return len-1;
}
const int N=105;
int n,m;
int f[26][26];
int g[N][N]; char s[N];
int F[N];
inline int calc(int a,int b){
int ret=0;
for (int x=a,t=__builtin_ctz(x&-x);x;x-=x&-x,t=__builtin_ctz(x&-x))
for (int y=b,s=__builtin_ctz(y&-y);y;y-=y&-y,s=__builtin_ctz(y&-y))
ret|=f[t][s];
return ret;
}
int main(){
char a[5]; int Q;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(m);
for (int i=1;i<=m;i++)
read(a),f[a[2]-'A'][a[3]-'A']|=1<<(a[1]-'A');
read(Q);
while (Q--){
n=read(s);
for (int i=1;i<=n;i++){
g[i][i]=1<<(s[i]-'A');
for (int j=i-1;j;j--){
g[j][i]=0;
for (int k=j;k<i;k++)
g[j][i]|=calc(g[j][k],g[k+1][i]);
}
}
F[0]=0;
for (int i=1;i<=n;i++){
F[i]=1<<30;
for (int j=1;j<=i;j++)
if (g[j][i]&(1<<18))
F[i]=min(F[i],F[j-1]+1);
}
if (F[n]!=(1<<30))
printf("%d\n",F[n]);
else
printf("NIE\n");
}
return 0;
}