UVa 11027 - Palindromic Permutation 回文串,组合加搜索
题目大意是,给你一串字符串,输出它的字典序的第n个回文字符串。如果没有或者没有n这么大的就输出“XXX”。
首先先考虑能不能构成回文串,首先奇数个的字母肯定只能有1个,不然肯定不能没办法构成回文串。然后要得到一个回文串,只需要算出(strlen(str)>> 1)这么长的字符串就行了,然后看看是否存在个数为奇数的字母,最后把算出来的字符串反向输出一下就OK了。
本来是打算用康托展开的,后来考虑到重复的元素是否能将重复的字母看成不重复的来算,最后花了一个下午,得出结论,不行。
最后百度了下,看了题解,原来要用搜索来做。
首先先计算一下所有的字母的个数,然后每个字母的个数直接除以2,用这个计算字符串的总排列数。接下来就直接搜索每一位,比如说对于第一位,先取出字典序最小的字母,计算第一个位置为这个字母的情况下又多少种排列数,比如说是k,如果k >= n,那么这个位置就可以确定下来了,如果k < n,, 那么这个位置放这个字母显然是不够的,需要放更大的字典序的字母进来,在继续找之前还要进行一步操作, n -= k, 因为如果对一个字符串进行全排列,肯定先排完第一个位置放字典序为最小的字母,然后才开始字典序第二大的。一直这样进行下去,这个字符串就搜索出来了。在这里写了个递归的代码,后来又改成了非递归的。
上代码:
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 typedef long long LL; 5 const int MAXN = 30 + 5; 6 7 LL fact[16]; 8 char OddChar; 9 int n, len, num[26]; 10 char str[MAXN], ans[MAXN]; 11 12 //计算有多少种 13 LL Permutation(int len) 14 { 15 LL res = fact[len]; 16 17 for(int i = 0; i < 26; ++i) 18 res /= fact[num[i]]; 19 20 return res; 21 } 22 23 LL MaxPalindromic() 24 { 25 memset(num, 0, sizeof(num)); 26 27 for(int i = 0; str[i] != '\0'; ++i) 28 ++num[str[i] - 'a']; 29 30 int OddNum = 0; 31 32 for(int i = 0; i < 26; ++ i) 33 { 34 if(num[i] & 1) 35 { 36 ++OddNum; 37 OddChar = i + 'a'; 38 } 39 40 num[i] >>= 1; 41 } 42 43 if(OddNum > 1) 44 return 0LL; 45 46 if(OddNum == 0) 47 OddChar = '#'; 48 49 len = strlen(str) >> 1; 50 return Permutation(len); 51 } 52 53 void solve(LL x, int n) 54 { 55 for(int i = 0; i < len; ++ i) 56 for(int j = 0; j < 26; ++ j) 57 if(num[j]) 58 { 59 --num[j]; 60 LL tmp = Permutation(n - 1 - i); 61 62 if(tmp < x) 63 { 64 ++num[j]; 65 x -= tmp; 66 continue; 67 } 68 69 ans[i] = j + 'a'; 70 break; 71 } 72 } 73 74 /* 75 void dfs(int cur, int n, LL x) 76 { 77 if(n == 0) 78 return; 79 80 for(int i = 0; i < 26; ++ i) 81 if(num[i]) 82 { 83 --num[i]; 84 LL tmp = Permutation(n - 1); 85 86 if(tmp < x) 87 { 88 ++num[i]; 89 x -= tmp; 90 continue; 91 } 92 93 ans[cur] = i + 'a'; 94 dfs(cur + 1, n - 1, x); 95 } 96 }*/ 97 98 int main() 99 { 100 int T; 101 scanf("%d", &T); 102 fact[0] = 1; 103 104 for(int i = 1; i < 16; ++ i) 105 fact[i] = fact[i - 1] * i; 106 107 for(int tCase = 1; tCase <= T; ++ tCase) 108 { 109 scanf("%s%d", str, &n); 110 printf("Case %d: ", tCase); 111 LL MaxNumber = MaxPalindromic(); 112 113 if(MaxNumber < n) 114 puts("XXX"); 115 else 116 { 117 //dfs(0, len, n); 118 solve(n, len); 119 120 for(int i = 0; i < len; ++ i) 121 putchar(ans[i]); 122 123 if(OddChar != '#') 124 putchar(OddChar); 125 126 for(int i = len - 1; i >= 0; -- i) 127 putchar(ans[i]); 128 129 putchar('\n'); 130 } 131 } 132 133 return 0; 134 }