[CF49E]Common ancestor
阿新 • • 發佈:2018-12-12
%d 時間 lin getchar 最小值 amp limit 相同 its
[CF49E]Common ancestor
題目大意:
有兩個由小寫字母構成的字符串\(S\)和\(T(|S|,|T|\le50)\)。另有\(n(n\le50)\)個形如\(a\to bc\)的信息,表示可以將字符\(a\)替換為\(bc\)。定義兩個字符串\(s,T\)的祖先\(R\)為能夠通過若幹次替換,使得其既可以變為\(S\),又可以變為\(T\)的字符串。求\(|R|\)的最小值。
思路:
首先分別預處理\(S,T\)中每一段是否可以通過單個字符轉化過來,如果能,可以從哪些字符開始轉化。然後枚舉\(S,T\)匹配的位置和最後一次匹配到的段,如果最後匹配到的段都可以表示成一個相同的字母,那麽就可以轉移。
時間復雜度\(\mathcal O(l^4+26^2l^3)\)。
源代碼:
#include<cstdio> #include<cctype> #include<cstring> #include<climits> #include<algorithm> inline int getint() { register char ch; while(!isdigit(ch=getchar())); register int x=ch^'0'; while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); return x; } inline char getalpha() { register char ch; while(!isalpha(ch=getchar())); return ch; } const int N=52,S=26; char s[N],t[N]; int map[S][S],f[2][N][N],g[N][N]; inline int idx(const char &ch) { return ch-'a'; } inline void solve(const char s[],const int &n,int f[N][N]) { for(register int i=1;i<=n;i++) { f[i][i]|=1<<idx(s[i]); } for(register int i=1;i<=n;i++) { for(register int j=i-1;j;j--) { for(register int k=j;k<i;k++) { for(register int a=0;a<26;a++) { if(!(f[j][k]>>a&1)) continue; for(register int b=0;b<26;b++) { if(!(f[k+1][i]>>b&1)) continue; f[j][i]|=map[a][b]; } } } } } } int main() { scanf("%s%s",&s[1],&t[1]); for(register int i=getint();i;i--) { const char a=getalpha(),b=getalpha(),c=getalpha(); map[idx(b)][idx(c)]|=1<<idx(a); } const int n=strlen(&s[1]),m=strlen(&t[1]); solve(s,n,f[0]); solve(t,m,f[1]); for(register int i=0;i<=n;i++) { for(register int j=0;j<=m;j++) { g[i][j]=INT_MAX; } } g[0][0]=0; for(register int i=1;i<=n;i++) { for(register int j=1;j<=m;j++) { for(register int k=1;k<=i;k++) { for(register int l=1;l<=j;l++) { if(!(f[0][k][i]&f[1][l][j])) continue; if(g[k-1][l-1]==INT_MAX) continue; g[i][j]=std::min(g[i][j],g[k-1][l-1]+1); } } } } printf("%d\n",g[n][m]==INT_MAX?-1:g[n][m]); return 0; }
[CF49E]Common ancestor