对于两个字符串$s$和$t$(保证其中每一种字符个数相同),定义$s$和$t$的相对逆序对数为$s$得到$t$的最少交换次数,显然同种字符相对顺序保持不变,因此即依次编号后的逆序对数
问题不妨看作构造合法字符串$t$使得$s$和$t$的相对逆序对数最小,定义$f_{S}(s)$为$s$仅保留$S$中的字符后所得到的字符串,那么有以下两个结论——
结论1:当$S={(,)}$时,若$t$是使得$s$和$t$相对逆序对数最小的合法字符串,则$f_{S}(t)$也是使得$f_{S}(s)$和$f_{S}(t)$相对逆序对数最小的合法字符串
结论2:当$S={o,x}$时,若$t$是使得$s$和$t$相对逆序对数最小的合法字符串,则$f_{S}(s)=f_{S}(t)$
由此,不妨先求出$S={(,)}$时的$f_{S}(t)$,进而即将$o$和$x$从左到右依次插入,显然这可以用一个二维dp计算,条件为$x$之前左括号数严格大于右括号数,计算答案考虑逆序对数即可
时间复杂度为$o(n^{2})$,可以通过
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 8005 4 queue<int>q; 5 vector<int>vb,vc; 6 int n,cnt,ans,sum[N],f[N][N]; 7 char s[N]; 8 int main(){ 9 scanf("%s",s+1); 10 n=strlen(s+1); 11 vb.push_back(0),vc.push_back(0); 12 for(int i=1;i<=n;i++){ 13 if (s[i]=='('){ 14 cnt++; 15 vb.push_back(i); 16 if (!q.empty()){ 17 vb.push_back(q.front()); 18 q.pop(); 19 } 20 } 21 if (s[i]==')'){ 22 cnt--; 23 if (cnt<0)q.push(i); 24 else vb.push_back(i); 25 } 26 if ((s[i]=='o')||(s[i]=='x'))vc.push_back(i); 27 } 28 for(int i=1;i<vb.size();i++){ 29 sum[i]=sum[i-1]; 30 if (s[vb[i]]=='(')sum[i]++; 31 else sum[i]--; 32 } 33 memset(f,0x3f,sizeof(f)); 34 memset(f[0],0,sizeof(f[0])); 35 for(int i=1;i<vc.size();i++){ 36 cnt=0; 37 for(int j=0;j<vb.size();j++) 38 if (vc[i]>vb[j])cnt++; 39 for(int j=0;j<vb.size();j++){ 40 if (vc[i]<vb[j])cnt++; 41 else cnt--; 42 if ((s[vc[i]]=='o')||(sum[j]))f[i][j]=min(f[i][j],f[i-1][j]+cnt); 43 } 44 for(int j=1;j<vb.size();j++)f[i][j]=min(f[i][j],f[i][j-1]); 45 } 46 ans=0x3f3f3f3f; 47 for(int i=0;i<vb.size();i++)ans=min(ans,f[(int)vc.size()-1][i]); 48 for(int i=1;i<vb.size();i++) 49 for(int j=i+1;j<vb.size();j++) 50 if (vb[i]>vb[j])ans++; 51 printf("%d ",ans); 52 return 0; 53 }