題目描述:
回文字符串是指從左到右和從右到左相同的字符串,現給定一個僅由小寫字母組成的字符串,你可以把它的字母重新排列,以形成不同的回文字符串。
輸入:非空僅由小寫字母組成的字符串,長度不超過100;
輸出:能組成的所有回文串的個數(因為結果可能非常大,輸出對1000000007取余數的結果)。
例如:輸入"aabb" 輸出為2(因為“aabb”對應的所有回文字符串有2個:abba和baab)
思路:
問題需要我們給出可行解,那么就需要梳理出清晰准確的因果關系:
在這26個字母的序列中,
Case<1>如果有大於1個元素的個數是奇數,那么無論如何都構不成回文
Case<2>如果有且僅有1個元素的個數是奇數,把它放在正中央,還是可以構成回文數的
Case<3>如果所有元素的個數都是偶數,亦可構成回文數
本質上可以把Case<1>和Case<2>當成同一情況予以處理
細節1:
對於可行的字符串,如何計算種數?
e.g. aaaabbcccddddee
因為回文串具有對稱性這一性質,所以只需要考慮一半的元素:aabcdde(多余的C放中央,不需考慮)
需要對其進行全排列,上例就是7!,顯然這7!包含重復的序列,如何歸為正解呢?應該除以每個元素個數一半的階乘,原因自己考慮
設有arr[26],存放的是每個元素出現的個數,M為字符串的元素總數
那么ans = (M/2)!/(arr[0]/2)!*(arr[1]/2)!*…(arr[25]/2)!
細節2:
如果取最大字符串,長度為100,我們就需計算50!
而以下數據類型的存儲范圍是:
int: 2147483647 (10位)------------12! = 479001600
long long(__int64):9223372036854775807 (19位)------------21! = 5.1090942e+19
所以如果暴力計算50!會溢出的
應對方法:對於ans = (M/2)!/(arr[0]/2)!*(arr[1]/2)!*…(arr[25]/2)!
不必先乘完再除,邊乘邊除
Source Code:
#include<stdio.h> #include<string.h> int arr[26]; char s[102]; int palindrome(const char *s) { int i,j,tmp,sum,item,death,len,count = 0; long long mul; memset(arr,0,sizeof(arr)); len = strlen(s);
for(i = 0; i < len; i++) arr[s[i]-97] += 1; for(i = 0; i < 26 ;i++){ tmp = arr[i]; if(tmp % 2 == 1) count++; if(tmp == 0 || tmp == 1) continue; sum = 1; for(j = 2; j <= tmp/2; j++) sum *= j; arr[i] = sum; }
if (count > 1) return 0; mul = 1,item = 0;
for(i = 1; i <= len/2; i++){ while((arr[item] == 0 || arr[item] == 1) && item < 26) item++; mul *= i; if( item < 26 && mul % arr[item] == 0){ mul /= arr[item]; item++; } if(item >= 26){ death = i; break; } } for(i = death+1; i<= len/2; i++){ mul = (mul*i) % 1000000007; } return mul; } int main(){ while(scanf("%s",s) != EOF){ printf("%d\n",palindrome(s)); } return 0; }