質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • Python 3.x

    9829questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Python 2.7

    1457questions

    Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

python3-文字列で隣り合う同じ文字を削除したい-

解決済

回答 6

投稿

  • 評価
  • クリップ 1
  • VIEW 805

n_oshiumi

score 10

 文字列から隣り合う同じ文字を削除したい

例えば、"aabcacc"という文字列があった場合
「aa」と「cc」を削除して 「bca」となるようにしたい。

"abbac"であれば、「bb」を削除したら「aac」になり、「aa」を削除して最終的に「c」になる
今回はこの"abbac"を想定してコードを書いています。

s="abbac"

a=[]
b=[]
for i in range(len(s)):
    a.append(s[i])

count = 0
while True:
    for j in range(len(a)-1):
        if a[j] == a[j+1]:
            b.append(a[j])
            b.append(a[j+1])
            count += 1
    if count == 0:
        break
    else:
        count = 0

set_ab = set(a) - set(b)
list_ab = list(set_ab)
print(list_ab)

 発生したエラー

  • ループから抜け出せない
    なぜこのコードでは抜け出せられないのでしょうか・・・?

  • 文字列の文字が操作できないらしい?(あまり詳しくないので、ネット情報です)
    文字列の文字の操作がreplace(s[0], "")のようにできないようだったので、
    遠回りかもしれませんが、一度一文字ずつリストに格納して、delで削除することにしました。

質問したいこと

  • 本当はリストを使うなんて遠回りしない方が良いのですが、リストを使わない場合、文字列の操作ができないという問題をどのように解決するのでしょうか?

  • なぜこのコードのbreakでループを終わらせられないのでしょうか?

*なにかアドバイスがあればどんなことでもいいのでお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    退会済みユーザー

    2018/02/24 20:32

    3文字連続してる場合はどうします?

    キャンセル

  • n_oshiumi

    2018/02/24 21:49

    "aaa"とあったら、ペア(aa)を削除するようにしていますので、aが残ります!

    キャンセル

回答 6

checkベストアンサー

+4

正規表現を使うといいと思います。

import re
a="abcddefgghiiiij"
b=re.sub("([a-z])\\1", "", a)
print(b)

追記:

"aaa"とあったら、ペア(aa)を削除するようにしていますので、aが残ります!

という質問者の追加コメントに対応してみます。「否定先読み」を使い、「a-zのどれかが存在し、直後に同じ文字が続き、しかしその直後にその文字は続かない」を表現します。

import re
a="abcddefgghiiiij"
b=re.sub("([a-z])\\1(?!\\1)", "", a)
print(b)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/24 22:09

    正規表現は難しくて、未だに上手くできないのですが、
    正規表現でこんなこともできることを知って、もう少し勉強してみようと思いました。
    ありがとうございました!

    キャンセル

  • 2018/02/25 17:03

    >>> print(re.sub("([a-z])\\1(?!\\1)", "", "aabcacc"))
    bca
    >>> print(re.sub("([a-z])\\1(?!\\1)", "", "abbac"))
    aac
    となります。
    これは質問者が求めている結果と一致してますか?

    キャンセル

  • 2018/02/25 17:10

    第2段落を完全に見落としてました。確かに他の回答にあるようなループ処理が必要になりそうですね。

    キャンセル

+3

リストを使わない場合

リストのようなコレクション的なものを作ってそれを加工するというのも別に悪くないのですが、「要素を一つ一つ流れ作業で料理する」という考え方が使えるような場合もかなり多いと思います。

functools.reduceを使った例を挙げてみます。reduceは要素を一つずつ取り出しては

... f(f(f(e0, e1), e2), e3) ...

というような関数の適用を繰り返して結果を求めてくれます。

fの第一引数は「前の要素までの簡約結果」で第二引数は「次の要素」です。本件の場合簡約結果を「連続した2つの文字を取り除いた結果」と考えると、以下のように書けます。

import functools

def drop_same(s, c):
  return s[:-1] if s != '' and s[-1] == c else s + c

functools.reduce(drop_same, 'abbac', '')      #  'c'
functools.reduce(drop_same, 'zabbaccz', '')   #  ''

Python 3.6.0

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/24 20:38

    興味でご質問したいのですが、対象も文字列に連続して3回文字が並ぶと1個残ってしまうのはどうしてですか?まだきちんとフローを終えてない状態で恐縮なのですが・・・

    キャンセル

  • 2018/02/24 20:43

    すいません、理解できました。マッチングが取れた時点で末尾と次の入力の両方を落としてるためですね。大変失礼しました。

    キャンセル

  • 2018/02/24 21:49

    いえいえ、質問ありがとうございます!!

    キャンセル

  • 2018/02/24 22:08

    ごめんなさい、自分への質問化と思って、返事をしたつもりでした、間違えました。。。
    functools.reduceは知らなかったので、タメになりました!ありがとうございました!

    キャンセル

  • 2018/02/24 22:15

    ペア単位で削除するか2個以上の連続を削除するかはあまりきにせず前者にしました。後者にするならまた別の工夫が必要ですね。仮にreduceを使うとしたらs[-1]がcと違うことがわかった時点でs[i:-1]が全部同じ文字になるようなiを求めてi<-1ならその範囲を削除・・・とかでしょうか。ややこしくてreduceの単純さがなくなっちゃいそうです。

    キャンセル

+2

whileでループが回るたびに、内側のforが文字列を最初から最後までスキャンしてしまっています。今の入力では、連続する文字列があるので、forを回す間にcountが1になってしまい、決してbreakには到達できなくなっていますよ。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/24 21:54

    あ、なるほど、確かにそうですね。
    最初にdelを使用してやってみたのですが、
    「list index out of range」というエラーが出ます。

    最初にrange(len(a)-1)とやっているのに、
    途中でリストの数が減って、計算が合わなくなったことが原因だと思われるのですが、
    このときの対処法がもし分かるようでしたら、教えていただきたいです。

    キャンセル

+1

自分で loop を回して処理する版と、正規表現を使う版を書いてみました。
replace.py

import re

def reduce_1(s):
    a = []
    b = []
    for i in range(len(s)):
        a.append(s[i])

    while True:
        j = 0
        while j < len(a):
            if j == len(a) - 1:
                b.append(a[j])
            elif (a[j] == a[j + 1]):
                j += 1
            else:
                b.append(a[j])
            j += 1

        if len(a) == len(b):
            break
        a = b
        b = []

    return b


def reduce_2(s):
    while True:
        ss = re.sub("(.)\\1", "", s)
        if s == ss:
            return ss
        s = ss


print(reduce_1("aabcacc"))
print(reduce_1("abbac"))

print(reduce_2("aabcacc"))
print(reduce_2("abbac"))

実行霊:
イメージ説明

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

itertools.groupbyを使うのはいかがでしょうか。

from itertools import groupby

def has_serial_duplicated_chars(src_str):
    return any(
        ch1 == ch2 for ch1, ch2 in zip(src_str, src_str[1:])
    )

def remove_serial_duplicated_chars(src_str):
    while has_serial_duplicated_chars(src_str):
        src_str = ''.join(
            k for k, gen in groupby(src_str) 
            if sum(1 for _ in gen) % 2 == 1
        )

    return src_str 


print(
    remove_serial_duplicated_chars('abbac')
)
print(
    remove_serial_duplicated_chars('abbbac')
)

実行結果 Wandbox

c
abac

過去の修正について

仕様の誤認が度重なり、二度にわたり大々的に回答を変更しています。
本来なら過去の回答に追記する形が良いのでしょうが、可読性を考慮し上書き修正いたしました。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

import re
def super_reduced_string(s):
    for i in range(len(s)):
        s=re.sub("([a-z])\\1", "", s)
        if len(s) == 0:
            print("Empty String")
            break
    print(s)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • Python 3.x

    9829questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • Python 2.7

    1457questions

    Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。