排列乘法

假设我们已有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]!='';++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]!='';++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]!='';++i)
    {
        if(src[i]==key)
        {
            return i;
        }
    }
    return -1;
}
int FindOneEle(char *src,char *flag,char key)
{
    int i;
    for(i=0;src[i]!='';++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("
");
}
View Code

逆序迭代

算法流程

  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("
");
    free(T);
}
View Code
原文地址:https://www.cnblogs.com/NoSoul/p/3303123.html