假设我们已有6个元素{a,b,c,d,e,f},我们要将这6个元素的一个序列改成另一个序列
比如:a b c d e f
---> c d f b e a
我们可以采用“循环记号”标记上面的改变
(acf)(bd)
表示为a-->c,c-->f,f-->a,b-->d,d-->b
在一个排列之后我们又可以应用另一个排序,我们可以将两个排列相乘
| a b c d e f | | a b c d e f | | a b c d e f |
| | * | | = | |
| c d f b e a | | b d c a f e | | c a e b f d |
显然排列乘法不满足交换率,采用循环记号,我们可以将上面的乘式写为:
(acf)(bd)(abd)(ef)=(acefb)
因此我们可以处理多项乘式相乘的结果。
E1.将乘法式中的右括号替换为与之对应的左括号后一个的字符
E2.从左到右找出没有被标记过的元素(所有元素都被标记了则算法结束),将该元素赋给Start,输出左括号以及该元素,并标记该元素
E3.将当前的算式的后一个元素赋给Current
E4.向右扫描算式,如果找到Current的元素,则返回E3.否则扫描至算式末尾
E5.判断Current==Start,如果不相等,标记Current,找到Current在算式最开始出现的地方.返回E3.
E6.输出右括号,返回E2
针对每一个未标记的元素根据算式遍历找到最终的替换元素,然后标记该元素,显然可以找出所有元素最终替换元素,复杂度为O(N*L)
void ChangeStr(char *str) { int i; char pre; for(i=0;str[i]!='\0';++i) { if(str[i]=='(') { pre=str[i+1]; } if(str[i]==')') { str[i]=pre; } } } int FindAnyEle(char *src,char *flag,char *start) { int i; for(i=0;src[i]!='\0';++i) { if(src[i]=='(') { continue; } if(!flag[src[i]-'a']) { *start=src[i]; printf("(%c",src[i]); flag[src[i]-'a']=1; return i; } } return -1; } int FindNextEle(char *src,char key,int from) { int i; for(i=from+1;src[i]!='\0';++i) { if(src[i]==key) { return i; } } return -1; } int FindOneEle(char *src,char *flag,char key) { int i; for(i=0;src[i]!='\0';++i) { if(src[i]==key) { printf("%c",key); flag[src[i]-'a']=1; return i; } } return -1; } void PermutationMul(int EleNum,char *SrcMulStr) { ChangeStr(SrcMulStr); char *Flag=(char *)malloc(EleNum*sizeof(char)); int i,id; char Start,Current; for(i=0;i<EleNum;++i) { Flag[i]=0; } id=FindAnyEle(SrcMulStr,Flag,&Start); while(~id) { while(~id) { Current=SrcMulStr[id+1]; id=FindNextEle(SrcMulStr,Current,id+1); } if(Current!=Start) { id=FindOneEle(SrcMulStr,Flag,Current); } else { printf(")"); id=FindAnyEle(SrcMulStr,Flag,&Start); } } free(Flag); printf("\n"); }
E1.初始化,对于1<=i<=N,将T[i]=i;T[i]表示i元素将被T[i]元素替换
E2.从右往左读取输入算式,读取下一个元素,如果输入结束,算法则也结束,如果元素为')',置Z=0重复E2;如果为‘(’,转去E4,否则转到E3
E3.交换Z与T[i],如果此时T[i]==0,将i赋值给j;返回E2
E4.将T[j]赋为Z;返回E2
一遍扫描迭代更新T[i]最终被替换的值,因为我们要找到最终被替换的值,所以要从右往左读,如果从左往右读可以迭代求出最终替换项被替换的值,比如最终T[1]=3,从右往左读的话,表示最终1要被3代替,从左往右读的话,表示最终3是被1代替.
显然通过逆序迭代每对括号,更新每对括号包含的替换信息,最终可以求出每个元素被替代的值,复杂度为O(L)
void PermutationMulEx(int EleNum,char *SrcMulStr) { int i,j,Z,Len,t; char *T=(char *)malloc(EleNum*sizeof(char)); for(i=0;i<EleNum;++i) { T[i]=i; } Len=strlen(SrcMulStr); for(i=Len-1;i>=0;--i) { if(SrcMulStr[i]==')') { Z=-1; continue; } if(SrcMulStr[i]=='(') { T[j]=Z; } else { t=Z; Z=T[SrcMulStr[i]-'a']; T[SrcMulStr[i]-'a']=t; if(T[SrcMulStr[i]-'a']==-1) { j=SrcMulStr[i]-'a'; } } } for(i=0;i<EleNum;++i) { printf("%d ",T[i]); } printf("\n"); free(T); }