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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Python 2.7

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

Python 3.x

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

Q&A

解決済

6回答

2669閲覧

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

n_oshiumi

総合スコア16

Python 2.7

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

Python 3.x

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

0グッド

1クリップ

投稿2018/02/24 09:59

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

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

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

python3

1s="abbac" 2 3a=[] 4b=[] 5for i in range(len(s)): 6 a.append(s[i]) 7 8count = 0 9while True: 10 for j in range(len(a)-1): 11 if a[j] == a[j+1]: 12 b.append(a[j]) 13 b.append(a[j+1]) 14 count += 1 15 if count == 0: 16 break 17 else: 18 count = 0 19 20set_ab = set(a) - set(b) 21list_ab = list(set_ab) 22print(list_ab) 23

発生したエラー

  • ループから抜け出せない

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

  • 文字列の文字が操作できないらしい?(あまり詳しくないので、ネット情報です)

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

###質問したいこと

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

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/02/24 11:32

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

2018/02/24 12:49

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

回答6

0

ベストアンサー

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

python

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

追記:

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

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

python

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

投稿2018/02/24 10:10

編集2018/02/24 21:17
KojiDoi

総合スコア13671

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

n_oshiumi

2018/02/24 13:09

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

2018/02/25 08:03

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

2018/02/25 08:10

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

0

リストを使わない場合

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

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

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

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

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

Python

1import functools 2 3def drop_same(s, c): 4 return s[:-1] if s != '' and s[-1] == c else s + c 5 6functools.reduce(drop_same, 'abbac', '') # 'c' 7functools.reduce(drop_same, 'zabbaccz', '') # '' 8

Python 3.6.0

投稿2018/02/24 11:14

編集2018/02/24 11:17
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/02/24 11:38

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

退会済みユーザー

2018/02/24 11:43

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

2018/02/24 12:49

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

2018/02/24 13:08

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

2018/02/24 13:15

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

0

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

投稿2018/02/24 10:30

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

n_oshiumi

2018/02/24 12:54

あ、なるほど、確かにそうですね。 最初にdelを使用してやってみたのですが、 「list index out of range」というエラーが出ます。 最初にrange(len(a)-1)とやっているのに、 途中でリストの数が減って、計算が合わなくなったことが原因だと思われるのですが、 このときの対処法がもし分かるようでしたら、教えていただきたいです。
guest

0

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

Python

1from itertools import groupby 2 3def has_serial_duplicated_chars(src_str): 4 return any( 5 ch1 == ch2 for ch1, ch2 in zip(src_str, src_str[1:]) 6 ) 7 8def remove_serial_duplicated_chars(src_str): 9 while has_serial_duplicated_chars(src_str): 10 src_str = ''.join( 11 k for k, gen in groupby(src_str) 12 if sum(1 for _ in gen) % 2 == 1 13 ) 14 15 return src_str 16 17 18print( 19 remove_serial_duplicated_chars('abbac') 20) 21print( 22 remove_serial_duplicated_chars('abbbac') 23)

実行結果 Wandbox

c abac

過去の修正について

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

投稿2018/02/25 06:56

編集2018/02/26 05:27
LouiS0616

総合スコア35660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

python

1 2import re 3 4def reduce_1(s): 5 a = [] 6 b = [] 7 for i in range(len(s)): 8 a.append(s[i]) 9 10 while True: 11 j = 0 12 while j < len(a): 13 if j == len(a) - 1: 14 b.append(a[j]) 15 elif (a[j] == a[j + 1]): 16 j += 1 17 else: 18 b.append(a[j]) 19 j += 1 20 21 if len(a) == len(b): 22 break 23 a = b 24 b = [] 25 26 return b 27 28 29def reduce_2(s): 30 while True: 31 ss = re.sub("(.)\1", "", s) 32 if s == ss: 33 return ss 34 s = ss 35 36 37print(reduce_1("aabcacc")) 38print(reduce_1("abbac")) 39 40print(reduce_2("aabcacc")) 41print(reduce_2("abbac"))

実行霊:
イメージ説明

投稿2018/02/25 06:48

katoy

総合スコア22324

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

python3

1 2import re 3def super_reduced_string(s): 4 for i in range(len(s)): 5 s=re.sub("([a-z])\1", "", s) 6 if len(s) == 0: 7 print("Empty String") 8 break 9 print(s)

投稿2018/02/24 13:12

n_oshiumi

総合スコア16

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問