2009-02-01 3 views
108

있으면 :점검 문자열 (문자열)리스트에서 요소 코드의 다음 블록

For I = 0 To listOfStrings.Count - 1 
    If myString.Contains(lstOfStrings.Item(I)) Then 
     Return True 
    End If 
Next 
Return False 

출력은 :

사례 1 :

myString: C:\Files\myfile.doc 
listOfString: C:\Files\, C:\Files2\ 
Result: True 

사례 2 :

myString: C:\Files3\myfile.doc 
listOfString: C:\Files\, C:\Files2\ 
Result: False 

목록 (listOfStrings)은 여러 항목 (최소 20)을 포함 할 수 있으며 수천 개의 문자열 (예 : myString)에 대해 검사해야합니다.

이 코드를 작성하는 더 나은 (효율적인) 방법이 있습니까?

답변

236
LINQ와

및 C#을 사용 (나는 많은 요즘 VB를 모르는) :

bool b = listOfStrings.Any(s=>myString.Contains(s)); 

또는 (더 짧고 더 효율적하지만 틀림없이 명확하지가) :

bool b = listOfStrings.Any(myString.Contains); 

하는 경우 당신은 평등을 테스트하고 있었고, HashSet 등을 살펴볼 가치가 있습니다.하지만 조각으로 분할하고 복잡성 순서를 추가하지 않는 한 부분 일치는 도움이되지 않습니다.


업데이트 : 실제로 "StartsWith"를 의미하는 경우 목록을 정렬하여 배열에 배치 할 수 있습니다. Array.BinarySearch을 사용하여 각 항목을 찾으십시오. 조회로 전체 또는 부분 일치인지 확인하십시오.

+0

처럼 대신 나는 그의 예에 따라 StartsWith를 사용하는 것 포함해야한다을 문자열 :

솔루션이었다. – tvanfosson

+0

@tvanfosson - 예제가 완전히 포함되었는지 여부에 따라 다르지만 예, 동의합니다. 물론 간단하게 변경할 수 있습니다. –

+0

이 코드는 알고리즘 수준에서 얼마나 더 효율적입니까? "Any"의 루프가 빠르지 만 짧고 빠르지 만 정확한 일치를 여러 번 수행해야하는 문제는 같습니다. –

5

이전 유사 질문 인 "Best way to test for existing string against a large list of comparables"의 제안이 많이있었습니다.

귀하의 요구 사항에는 정규 표현으로 충분할 수 있습니다. 표현식은 모든 후보 부분 문자열을 OR "|"연산자로 연결 한 것입니다. 물론 표현식을 만들 때 이스케이프 처리되지 않은 문자를주의해야하거나 복잡성이나 크기 제한으로 인해 컴파일하지 못하게해야합니다.

이렇게하는 또 다른 방법은 모든 후보 부분 문자열을 나타내는 trie data structure을 구성하는 것입니다.이 방법은 정규식 정규 표현식이 수행하는 작업과 다소 중복 될 수 있습니다. 테스트 문자열의 각 문자를 단계별로 실행하면 트리의 루트에 대한 새 포인터를 만들고 해당 포인터 (존재하는 경우)로 포인터를 이동합니다. 포인터가 잎에 닿으면 일치를 얻습니다.

2

패턴을 기반으로 한 개선 사항은 포함 대신 StartsWith를 사용하는 것으로 바뀌 었습니다. StartsWith는 첫 번째 불일치를 찾을 때까지 각 문자열을 반복 할 필요가있을 때마다 모든 문자 위치에서 검색을 다시 시작하지 않아도됩니다.

또한 패턴에 따라 myString 경로의 첫 번째 부분을 추출한 다음 비교를 되돌릴 수 있습니다. 즉, 문자열 대신 문자열 목록에서 myString의 시작 경로를 찾고 다른 방법으로.

string[] pathComponents = myString.Split(Path.DirectorySeparatorChar); 
string startPath = pathComponents[0] + Path.DirectorySeparatorChar; 

return listOfStrings.Contains(startPath); 

편집 : 당신이 ContainsKey-Contains을 변경할 수 있으며, 조회가 O 것 때문에이 @Marc Gravell 언급 HashSet의 아이디어를 사용하여 더 빠른 것 (1) 대신 O의 (N). 경로가 정확히 일치하는지 확인해야합니다. 이것은 @Marc Gravell 's와 같은 일반적인 해결책이 아니라 예제에 맞게 작성되었습니다.

죄송합니다 C# 예입니다. 나는 VB로 번역하기에 충분한 커피가 없다.

+0

다시 시작합니다; 아마도 미리 정렬하고 이진 검색을 사용합니까? 그것은 더 빠를 수도 있습니다. –

0

속도가 중요한 경우 패턴 집합에 대해 Aho-Corasick algorithm을 찾아야 할 수 있습니다.

오류 링크가있는입니다. 즉, 복잡도는 O (n + m + k)입니다. 여기서 n은 입력 텍스트의 길이이고, m은 패턴의 누적 길이이며, k는 일치 횟수입니다. 첫 번째 일치 항목이 발견 된 후 종료되도록 알고리즘을 수정하면됩니다.

1

속도를 테스트 했습니까?

즉 샘플 데이터 세트를 생성하고 프로파일 링 했습니까? 그것은 당신이 생각하는 것만 큼 나쁘지 않을 수도 있습니다.

이것은 또한 별도의 스레드로 생성하여 속도의 환영을 줄 수있는 것일 수도 있습니다!

0
myList.Any(myString.Contains); 
1

나는 Marc의 대답을 좋아하지만, CaSe InSenSiTiVe로 일치하는 Contains가 필요했습니다. 당신이 만들 때

bool b = listOfStrings.Any(s => myString.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0)) 
+0

> -1이 아니어야합니까? – CSharped

+0

@CSharped> -1 (마이너스 1 이상) 및> = 0 (0 이상) 같은 것은 중요하지 않습니다. – WhoIsRich

3

당신이이

bool inact = new string[] { "SUSPENDARE", "DIZOLVARE" }.Any(s=>stare.Contains(s)); 
관련 문제