2 votes

Suppression permutée de sous-chaînes définies de longueur variable à partir de chaînes de caractères

J'essaie de générer toutes les permutations à partir d'une liste de chaînes où certaines sous-chaînes de caractères sont supprimées. Je dispose d'une liste de certaines compositions chimiques et je souhaite obtenir toutes les compositions résultant de cette liste lorsque l'un de ces éléments est supprimé. Un court extrait de cette liste ressemble à ceci :

AlCrHfMoNbN
AlCrHfMoTaN
AlCrHfMoTiN
AlCrHfMoVN
AlCrHfMoWN
...

Ce que j'essaie d'obtenir, c'est

 AlCrHfMoNbN --> CrHfMoNbN
                 AlHfMoNbN
                 AlCrMoNbN
                 AlCrHfNbN
                 AlCrHfMoN
AlCrHfMoTaN -->  CrHfMoTaN
                 AlHfMoTaN
                 AlCrMoTaN
                 AlCrHfTaN
                 AlCrHfMoN

pour chaque composition. Je n'ai besoin que de la bonne colonne. Comme vous pouvez le constater, certaines des compositions obtenues sont des doublons, ce qui est voulu. La liste des éléments à supprimer est la suivante

Al, Cr, Hf, Mo, Nb, Ta, Ti, V, W, Zr

Comme vous le voyez, certains ont une longueur de deux caractères et d'autres d'un seul.

Il existe une question qui porte sur un sujet très similaire, mais mon problème est plus complexe : Obtention d'une liste de chaînes de caractères dont le caractère a été supprimé par permutation

J'ai essayé d'adapter le code à mes besoins :

def f(s, c, start):
    i = s.find(c, start)
    return [s] if i < 0 else f(s, c, i+1) + f(s[:i]+s[i+1:], c, i)

s = 'AlCrHfMoNbN'
print(f(s, 'Al', 0))

Mais cette approche simple n'aboutit qu'à ['AlCrHfMoNbN', 'lCrHfMoNbN'] . Ainsi, un seul caractère est supprimé, alors que j'ai besoin de supprimer une chaîne définie de caractères de longueur variable. De plus, je suis limité à un seul objet d'entrée s - au lieu de centaines que je dois traiter - il n'est donc pas possible de les passer en revue à la main.


En résumé, ce dont j'ai besoin, c'est d'une modification du code qui permette de.. :

  • entrée d'une liste de chaînes de caractères séparées par des sauts de ligne ou des espaces blancs
  • supprimer les sous-chaînes de caractères de cette liste qui sont définies par une deuxième liste (comme ci-dessus)
  • écrit les éléments "réduits" qui en résultent dans une liste continue, de préférence dans une seule colonne sans virgule et autres.

Comme je n'ai qu'une certaine expérience de Python et de Bash, je préfère de loin une solution utilisant ces langages.

3voto

Chris Points 2347

IIUC, il suffit de str.replace :

input_list = ['AlCrHfMoNbN', 'AlCrHfMoTaN']
removals = ['Al', 'Cr', 'Hf', 'Mo', 'Nb', 'Ta', 'Ti', 'V', 'W', 'Zr']
result = {}
for i in input_list:
    result[i] = [i.replace(r,'') for r in removals if r in i]    

Salida:

{'AlCrHfMoNbN': ['CrHfMoNbN',
  'AlHfMoNbN',
  'AlCrMoNbN',
  'AlCrHfNbN',
  'AlCrHfMoN'],
 'AlCrHfMoTaN': ['CrHfMoTaN',
  'AlHfMoTaN',
  'AlCrMoTaN',
  'AlCrHfTaN',
  'AlCrHfMoN']}

1voto

oguz ismail Points 34195

Si vous avez gawk, mettez FPAT a [A-Z][a-z]* afin que chaque élément soit considéré comme un champ, et utiliser une simple boucle pour générer des permutations. définir également OFS à une chaîne vide, de sorte qu'il n'y ait pas d'espaces dans les enregistrements de sortie.

$ gawk 'BEGIN{FPAT="[A-Z][a-z]*";OFS=""} {for(i=1;i<NF;++i){p=$i;$i="";print;$i=p}}' file
CrHfMoNbN
AlHfMoNbN
AlCrMoNbN
AlCrHfNbN
AlCrHfMoN
CrHfMoTaN
AlHfMoTaN
AlCrMoTaN
AlCrHfTaN
AlCrHfMoN
CrHfMoTiN
AlHfMoTiN
AlCrMoTiN
AlCrHfTiN
AlCrHfMoN
CrHfMoVN
AlHfMoVN
AlCrMoVN
AlCrHfVN
AlCrHfMoN
CrHfMoWN
AlHfMoWN
AlCrMoWN
AlCrHfWN
AlCrHfMoN

J'ai également rédigé une version portable avec des espaces supplémentaires et des commentaires explicatifs :

awk '{
  # separate last element from others
  sub(/[A-Z][a-z]*$/, " &")
  # from the beginning of line
  # we will match each element and print a line where it is omitted
  for (i=0; match(substr($1,i), /[A-Z][a-z]*/); i+=RLENGTH)
    print substr($1,1,i)  substr($1,i+RLENGTH+1) $2
    #     ^ before match  ^ after match          ^ last element
}' file

0voto

Nemo Points 13

Cette méthode n'utilise pas votre tentative, mais elle fonctionne si nous supposons que vos éléments commencent toujours par une lettre majuscule (et qu'ils ne sont constitués que de lettres minuscules) :

def f(s):
    # split string by elements
    import re
    elements = re.findall('[A-Z][^A-Z]*', s)

    # make a list of strings, where the first string has the first element removed, the second string the second, ...
    r = []
    for i in range(len(elements)):
        r.append(''.join(elements[:i]+elements[i+1:]))

    # return this list
    return r

Bien entendu, cela ne fonctionne que pour une seule corde. Donc, si vous avez une liste de chaînes l et que vous voulez l'appliquer à chacune d'entre elles, utilisez simplement une boucle for comme celle-ci :

# your list of strings
l = ["AlCrHfMoNbN", "AlCrHfMoTaN", "AlCrHfMoTiN", "AlCrHfMoVN", "AlCrHfMoWN"]

# iterate through your input list
for s in l:
    # call above function
    r = f(s)
    # print out the result if you want to
    [print(i) for i in r]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X