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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

最適化

最適化とはメソッドやデザインの最適な処理方法を選択することです。パフォーマンスの向上を目指す為に行われます。プログラミングにおける最適化は、アルゴリズムのスピードアップや、要求されるリソースを減らすことなどを指します。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Q&A

解決済

3回答

2360閲覧

文字列の比較の高速化

grape_ll

総合スコア83

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

最適化

最適化とはメソッドやデザインの最適な処理方法を選択することです。パフォーマンスの向上を目指す為に行われます。プログラミングにおける最適化は、アルゴリズムのスピードアップや、要求されるリソースを減らすことなどを指します。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

0グッド

1クリップ

投稿2020/08/10 11:39

質問内容

AtCoderの以下のリンクにおける過去問を解いていました.
最初に書いたコード1ではTLEとなってしまい,解説をみてソートをしてから比較しないとTLEになってしまうと書いてあったので,コード2ではソートしてから比較するようにしてみたのですが,コード1よりもTLEになってしまうケースが増えてしまいました.どのようなところが実行時間を引き延ばしている原因なのか教えていただきたいです.

問題

コード1

C

1#include<stdio.h> 2#include<stdlib.h> 3#include<string.h> 4int main(void){ 5 int N; 6 char **str; 7 int count=1; 8 9 scanf("%d",&N); 10 str=(char **)malloc(sizeof(char *)*N); 11 for(int i=0;i<N;i++){ 12 str[i]=(char *)malloc(sizeof(char)*11); 13 } 14 for(int i=0;i<N;i++){ 15 scanf("%s",str[i]); 16 for(int j=0;j<i;j++){ 17 if(strcmp(str[i],str[j])==0) break; 18 if(j==i-1) count++; 19 } 20 } 21 printf("%d\n",count); 22 return 0; 23} 24

コード2

C

1#include<stdio.h> 2#include<stdlib.h> 3#include<string.h> 4 5 char **str; 6 7void sort(int num){ 8 char tmp[11]; 9 for(int i=1;i<num;i++){ 10 for(int j=1;j<num;j++){ 11 if(strcmp(str[j-1], str[j])>0){ 12 strcpy(tmp, str[j-1]); 13 strcpy(str[j-1], str[j]); 14 strcpy(str[j], tmp); 15 } 16 } 17 } 18 19} 20int main(void){ 21 int N; 22 int count=1; 23 24 scanf("%d",&N); 25 str=(char **)malloc(sizeof(char *)*N); 26 for(int i=0;i<N;i++){ 27 str[i]=(char *)malloc(sizeof(char)*11); 28 } 29 for(int i=0;i<N;i++) scanf("%s",str[i]); 30 sort(N); 31 for(int i=1;i<N;i++){ 32 if(strcmp(str[i],str[i-1])!=0) count++; 33 } 34 printf("%d\n",count); 35 return 0; 36} 37

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

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

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

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

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

guest

回答3

0

ベストアンサー

どのようなところが実行時間を引き延ばしている原因なのか教えていただきたいです.

重複を除いて勘定するトコは(loopの入れ子がなくなることで)確実に早くなってるでしょうから、
「ソート自体に時間を食ってる」と考えられます。

sort(N); 改め、もっと速い
qsort(str, N, sizeof(char*),(int()(const void,const void*))&strcmp);
に置き換えてはいかがでしょう。

[追記] sort() 内の文字列の交換:

strcpy(tmp, str[j - 1]); strcpy(str[j - 1], str[j]); strcpy(str[j], tmp);

において、文字列のコピーを三回やってるのが時間を食ってるかもです。
コピーを行わずポインタを入れ替えるだけ↓にすれば、かなり速くなる可能性があります。
※ qsort には敵わんでしょうけど

char* tmp = str[j-1]; str[j-1] = str[j]; str[j] = tmp;

[追記]

qsort(str, N, sizeof(char*),(int()(const void,const void*))&strcmp);

に置き換えてはいかがでしょう。

ごめんなさい、これマチガイ。
正しくは:

C

1int str_compare(const void* pa, const void* pb) { 2 const char* a = *(const char**)pa; 3 const char* b = *(const char**)pb; 4 return strcmp(a, b); 5}

を定義したうえで
qsort(str, N, sizeof(char*), &str_compare);
に置き換え、です。

おわびついでに、質問に呈示されたsort(バブルソートかな)で
ランダムシャフルされた 要素数10000 / 文字列長9 の配列をソートしてみました。

Windows10(64bit) / ぽんこつ i7-2600K で 1800[ms]
問題で定められた制限時間ギリギリです、問題にあった最大要素数200000だととうてい間に合いません。

標準関数 qsort なら要素数 200000 でも80[ms] でソート完了。

投稿2020/08/10 12:39

編集2020/08/11 06:39
episteme

総合スコア16614

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

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

asm

2020/08/10 14:51

ソートアルゴリズムがバブルソートより遅いんで 競プロやるのならソートアルゴリズムくらい勉強しといた方がいいんじゃないですかね
episteme

2020/08/10 23:57

Cで競プロはツラそう...
grape_ll

2020/08/12 07:01

sortの速度も気にしなくてはいけないのですね. 教えていただいたものを使って試してみようと思います.
grape_ll

2020/08/12 07:19

教えていただいたqsortでTLEの問題は解決しました.ありがとうございます. この質問の大本が解決したうえでの質問で申し訳ないのですが,改良したコードでは今度は不正解がいくつかのパターンで出てしまうようになったのですが,なぜこのようなことが発生するのか教えていただけませんでしょうか.sortの部分しか触れていないのに,以前の二つのコードで正解していたところが不正解になってしまう理由が分かりませんでした. #include<stdio.h> #include<stdlib.h> #include<string.h> char **str; int str_compare(const void* left,const void* right){ char *left_char=(char *)left; char *right_char=(char *)right; return strcmp(left_char,right_char); } int main(void){ int N; int count=1; scanf("%d",&N); str=(char **)malloc(sizeof(char *)*N); for(int i=0;i<N;i++){ str[i]=(char *)malloc(sizeof(char)*11); } for(int i=0;i<N;i++) scanf("%s",str[i]); qsort(str,N,sizeof(char*),&str_compare); for(int i=1;i<N;i++){ if(strcmp(str[i],str[i-1])!=0) count++; } //for(int i=0;i<N;i++) printf("%s\n",str[i]); printf("%d\n",count); return 0; }
episteme

2020/08/12 07:30

char *left_char=(char *)left; char *right_char=(char *)right; ココ↑、正しくは↓ char *left_char=*(char **)left; char *right_char=*(char **)right;
grape_ll

2020/08/12 11:05

https://qiita.com/goforbroke/items/422861c4af9b155e8f56 このサイトも参考にして書かせて戴いてました. ご指摘通り *を増やしてみても不正解の問題は減らなかったようなので,他にも何か原因になりそうな点があれば教えていただきたいです. よろしくお願いいたします.
episteme

2020/08/12 14:07

問題なさそうなんやけどな... sortのあと、str[0]~str[N-1]は確かにソートされていますか?
grape_ll

2020/08/16 11:03

解決できたみたいです!ありがとうございました!
episteme

2020/08/16 11:33

「みたいです」って? なんかバグってた?
grape_ll

2020/08/16 11:58

すいません,言い方が悪かったですね. きちんと解決できたのでバグ等はございませんでした. ありがとうございます.
episteme

2020/08/16 11:59

> *を増やしてみても不正解の問題は減らなかったようなので,他にも何か原因になりそうな点があれば教えていただきたいです. コレ↑は何だったんです?
grape_ll

2020/08/16 12:07

頭を冷やすために時間を空けて,他の方法で完了させてしまいました. お手数おかけしてしまい申し訳ございません. ちなみにstrのソートは正しく実行できていました.
guest

0

Cではやりたくない問題ですね。Rubyなら7行で済みますから。

まず、解き方が間違っています。文字列を一つ一つ比較していくとなるとO(N^2)の時間計算量が必要です。これではどんなに頑張っても時間内に終わらすのは難しいです。しかし、この問題はO(N)に抑えることができます。

どうすれば良いのかというとハッシュ値を使います。ハッシュテーブルは聞いたことがありますか?そうです、ハッシュテーブルの仕組みと同じような物を使えば、前と同じ文字列があるかどうかがO(1)で判断できます。その繰り返しなので、最終的にはO(N)になると言うことです。

他言語であれば、C++ならstd::unordered_set、Javaならjava.util.HashSet、Pythonならset、RubyならSetを、JavaScriptならSetというものが標準で用意されていますので、こちらを使うと簡単に書けます。しかし、Cにはこのような便利なオブジェクトの型は用意されていません。ですので、自分で作る必要があります。

ハッシュテーブル、セット、等のキーワードでCでの実装方法を探してみて下さい。

投稿2020/08/10 12:24

編集2020/08/10 12:45
raccy

総合スコア21735

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

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

episteme

2020/08/10 12:47 編集

C++では: ハッシュ :  std::unordered_set 二分木 :  std::set
raccy

2020/08/10 12:44

あ・・・
raccy

2020/08/10 12:48

> epistemeさん サンクスです。直したっす。 ハッシュじゃなくて二分木でも間に合いそうな気がしますが、O(N log N)なのでちょっと遅い?うーん、このあたりは難しくてわからないです。
episteme

2020/08/10 12:54

問題の設定では 20万要素を2秒間 ですから O(N logN) ならいまどきの計算機でイケそうな。
grape_ll

2020/08/12 07:00

ご回答ありがとうございます. Javaを勉強していたのでHashSetを扱ったことはあります.しかし,Cだと用意されていないのですね. 調べて作ってみようと思います.
guest

0

Pythonでは1行で済みますけどね。
https://atcoder.jp/contests/abc164/submissions/41716460

投稿2023/05/27 03:40

hiro1729

総合スコア2

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問