T-SQL을 사용하여 문자열이 회문인지 테스트


24

저는 T-SQL의 초보자입니다. 입력 문자열이 회문인지 여부를 결정하고 싶습니다. 그렇지 않으면 출력 = 0이고 출력 = 1입니다. 나는 아직도 구문을 알아 내고있다. 오류 메시지가 표시되지 않습니다. 저는 T-SQL의 작동 방식에 대한 이해와 지식을 얻고 더 나은 결과를 얻기 위해 다양한 솔루션과 피드백을 찾고 있습니다. 저는 여전히 학생입니다.

내가 본 것처럼 핵심 아이디어는 가장 왼쪽과 오른쪽 문자를 서로 비교하고 평등을 확인한 다음 왼쪽의 두 번째 문자와 마지막 문자의 두 번째 문자 등을 비교하는 것입니다. 우리는 루프를 수행합니다 : 문자가 서로 같으면 계속 진행합니다. 끝에 도달하면 1을 출력하고 그렇지 않으면 0을 출력합니다.

당신은 비판을 주시겠습니까?

CREATE function Palindrome(
    @String  Char
    , @StringLength  Int
    , @n Int
    , @Palindrome BIN
    , @StringLeftLength  Int
)
RETURNS Binary
AS
BEGIN
SET @ n=1
SET @StringLength= Len(String)

  WHILE @StringLength - @n >1

  IF
  Left(String,@n)=Right(String, @StringLength)

 SET @n =n+1
 SET @StringLength =StringLength -1

 RETURN @Binary =1

 ELSE RETURN @Palindrome =0

END

나는 올바른 길을 가고 있다고 생각하지만 여전히 먼 길을 가고 있습니다. 어떤 아이디어?


LTRIM(RTRIM(...))공백?
WernerCD

답변:


60

SQL Server를 사용하는 경우 REVERSE () 함수를 사용하여 확인할 수 있습니까?

SELECT CASE WHEN @string = REVERSE(@String) THEN 1 ELSE 0 END AS Palindrome;

Martin Smith의 의견을 포함하여 SQL Server 2012 이상에서는 IIF () 함수를 사용할 수 있습니다 .

SELECT IIF(@string = REVERSE(@String),1,0) AS Palindrome;

17

상당히 많은 해결책이 있기 때문에 나는 당신의 질문의 "비평"부분으로 갈 것입니다. 몇 가지 메모 : 나는 오타를 수정하고 내가 한 곳을 지적했다. 내가 오타가되는 것에 대해 틀렸다면 의견에 언급하고 진행 상황을 설명하겠습니다. 나는 당신이 이미 알고있을 수있는 몇 가지 사항을 지적 할 것이므로, 그렇게한다면 공격하지 마십시오. 일부 의견은 까다로워 보일 수 있지만 여행 중 어디에 있는지 모르므로 처음 시작한다고 가정해야합니다.

CREATE function Palindrome (
    @String  Char
    , @StringLength  Int
    , @n Int
    , @Palindrome BIN
    , @StringLeftLength  Int

항상 길이는 char또는 varchar정의를 포함합니다 . Aaron Bertrand 는 여기서 자세히 이야기합니다 . 그는 이야기하고 varchar있지만 같은 것입니다 char. varchar(255)상대적으로 짧은 문자열 만 원하거나 varchar(8000)큰 문자열 또는 심지어 는 문자열을 원할 경우 이것을 사용합니다 varchar(max). Varchar가변 길이 문자열 char은 고정 문자열 전용입니다. 사용중인 문자열의 길이를 확실하지 않기 때문에 varchar. 또한 binary아닙니다 bin.

다음으로 모든 변수를 매개 변수로 넣을 필요는 없습니다. 코드 내에서 선언하십시오. 전달할 계획 인 경우에만 매개 변수 목록에 무언가를 넣으십시오. (이것이 마지막에 어떻게 보이는지 볼 것입니다.) 또한 @StringLeftLength가 있지만 절대 사용하지 마십시오. 그래서 나는 그것을 선언하지 않을 것입니다.

다음으로해야 할 일은 몇 가지 사항을 명확하게하기 위해 조금 다시 포맷하는 것입니다.

BEGIN
    SET @n=1
    SET @StringLength = Len(@String) -- Missed an @

    WHILE @StringLength - @n >1 
        IF Left(@String,@n)=Right(@String, @StringLength) -- More missing @s
            SET @n = @n + 1 -- Another missing @

    SET @StringLength = @StringLength - 1  -- Watch those @s :)

    RETURN @Palindrome = 1 -- Assuming another typo here 

    ELSE 
        RETURN @Palindrome =0

END

들여 쓰기를 수행 한 방식을 보면 내가 이것을 가지고 있음을 알 수 있습니다.

    WHILE @StringLength - @n >1 
        IF Left(@String,@n)=Right(@String, @StringLength)
            SET @n = @n + 1

그 이유는 명령 이 첫 번째 코드 줄 WHILE과 같고 IF영향을 받기 때문 입니다. BEGIN .. END여러 명령을 원할 경우 블록 을 사용해야 합니다. 그래서 우리가 얻는 고정 :

    WHILE @StringLength - @n > 1 
        IF Left(@String,@n)=Right(@String, @StringLength)
            BEGIN 
                SET @n = @n + 1
                SET @StringLength = @StringLength - 1
                RETURN @Palindrome = 1 
            END
        ELSE 
            RETURN @Palindrome = 0

BEGIN .. END블록 만 추가 한 것을 알 수 IF있습니다. 그이기 때문에 비록 IF문은 여전히합니다 (수행 모든 것을 포괄하는 하나의 명령문 긴 여러 줄입니다 (심지어 여러 명령을 포함) IFELSE문장의 일부).

다음에 두 가지 후에 오류가 발생합니다 RETURNs. 변수 또는 리터럴을 반환 할 수 있습니다. 변수를 설정하고 동시에 반환 할 수 없습니다.

                SET @Palindrome = 1 
            END
        ELSE 
            SET @Palindrome = 0

    RETURN @Palindrome

이제 우리는 논리에 있습니다. 먼저 사용하고있는 LEFTRIGHT기능이 훌륭 하다는 것을 지적 하지만 요청 된 방향에서 전달하는 문자 수를 알려줍니다. "test"라는 단어를 전달했다고 가정 해 봅시다. 첫 번째 패스에서 이것을 얻습니다 (변수 제거).

LEFT('test',1) = RIGHT('test',4)
    t          =      test

LEFT('test',2) = RIGHT('test',3)
    te         =      est

분명히 그것은 당신이 기대 한 것이 아닙니다. 당신은 정말로 substring대신 에 사용하고 싶을 것입니다. 부분 문자열을 사용하면 시작 지점뿐만 아니라 길이도 전달할 수 있습니다. 그래서 당신은 얻을 것입니다 :

SUBSTRING('test',1,1) = SUBSTRING('test',4,1)
         t            =         t

SUBSTRING('test',2,1) = SUBSTRING('test',3,1)
         e            =         s

다음으로 IF 문의 한 가지 조건에서만 루프에서 사용하는 변수를 증가시킵니다. 해당 증분에서 변수 증분을 완전히 빼냅니다. 추가 BEGIN .. END블록 이 필요 하지만 다른 블록을 제거해야합니다.

        WHILE @StringLength - @n > 1 
            BEGIN
                IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
                    SET @Palindrome = 1 
                ELSE 
                    SET @Palindrome = 0

                SET @n = @n + 1
                SET @StringLength = @StringLength - 1
            END

WHILE마지막 테스트를 위해 조건 을 변경해야합니다 .

        WHILE @StringLength > @n 

그리고 마지막으로, 현재 상태는 홀수 개의 문자가있는 경우 마지막 문자를 테스트하지 않습니다. 예를 들어 'ana'를 사용하면 n테스트되지 않습니다. 괜찮습니다. 단어 글자를 설명해야합니다 (긍정적 인 단어로 계산하려면). 값을 미리 설정하면됩니다.

그리고 이제 우리는 마침내 :

CREATE FUNCTION Palindrome (@String  varchar(255)) 
RETURNS Binary
AS

    BEGIN
        DECLARE @StringLength  Int
            , @n Int
            , @Palindrome binary

        SET @n = 1
        SET @StringLength = Len(@String)
        SET @Palindrome = 1

        WHILE @StringLength > @n 
            BEGIN
                IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
                    SET @Palindrome = 1 
                ELSE 
                    SET @Palindrome = 0

                SET @n = @n + 1
                SET @StringLength = @StringLength - 1
            END
        RETURN @Palindrome
    END

마지막 의견. 나는 일반적인 형식의 팬입니다. 실제로 코드 작동 방식을 확인하고 실수를 지적하는 데 도움이 될 수 있습니다.

편집하다

Sphinxxx가 언급했듯이 여전히 논리에 결함이 있습니다. 일단 우리를 치고 0으로 ELSE설정 @Palindrome하면 계속할 점이 없습니다. 실제로 그 시점에서 우리는 단지 할 수있었습니다 RETURN.

                IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
                    SET @Palindrome = 1 
                ELSE 
                    RETURN 0

우리가 현재 @Palindrome"이것은 회문이다"라는 목적으로 만 사용 하고 있다는 점을 감안할 때 실제로 그것을 가질 필요는 없습니다. 변수를 제거 하고 루프를 통과하는 경우에만 논리를 단락 시 ( RETURN 0) 및 RETURN 1(긍정적 응답) 단락으로 전환 할 수 있습니다 . 이것이 실제로 우리의 논리를 다소 단순화한다는 것을 알 수 있습니다.

CREATE FUNCTION Palindrome (@String  varchar(255)) 
RETURNS Binary
AS

    BEGIN
        DECLARE @StringLength  Int
            , @n Int

        SET @n = 1
        SET @StringLength = Len(@String)

        WHILE @StringLength > @n 
            BEGIN
                IF SUBSTRING(@String,@n,1) <> SUBSTRING(@String, @StringLength,1)
                    RETURN 0

                SET @n = @n + 1
                SET @StringLength = @StringLength - 1
            END
        RETURN 1
    END

15

숫자 테이블 방식을 사용할 수도 있습니다.

보조 번호 테이블이없는 경우 다음과 같이 보조 번호 테이블을 만들 수 있습니다. 이것은 백만 개의 행으로 채워져 있으므로 최대 2 백만 자의 문자열 길이에 적합합니다.

CREATE TABLE dbo.Numbers (number int PRIMARY KEY);

INSERT INTO dbo.Numbers
            (number)
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM   master..spt_values v1,
       master..spt_values v2 

아래는 왼쪽의 각 문자를 오른쪽의 해당 파트너와 비교하며, 불일치가 발견되면 단락 및 0을 반환 할 수 있습니다. 문자열이 홀수 길이 인 경우 중간 문자는 검사되지 않으므로 결과가 변경되지 않습니다 .

DECLARE @Candidate VARCHAR(MAX) = 'aibohphobia'; /*the irrational fear of palindromes.*/

SET @Candidate = LTRIM(RTRIM(@Candidate)); /*Ignoring any leading or trailing spaces. 
                      Could use `DATALENGTH` instead of `LEN` if these are significant*/

SELECT CASE
         WHEN EXISTS (SELECT *
                      FROM   dbo.Numbers
                      WHERE  number <= LEN(@Candidate) / 2
                             AND SUBSTRING(@Candidate, number, 1) 
                              <> SUBSTRING(@Candidate, 1 + LEN(@Candidate) - number, 1))
           THEN 0
         ELSE 1
       END AS IsPalindrome 

어떻게 작동하는지 잘 모르겠다면 아래에서 볼 수 있습니다.

DECLARE @Candidate VARCHAR(MAX) = 'this is not a palindrome';

SELECT SUBSTRING(@Candidate, number, 1)                       AS [Left],
       SUBSTRING(@Candidate, 1 + LEN(@Candidate) - number, 1) AS [Right]
FROM   dbo.Numbers
WHERE  number <= LEN(@Candidate) / 2; 

여기에 이미지 설명을 입력하십시오

이것은 기본적으로 질문에 설명 된 것과 동일한 알고리즘이지만 반복 절차 코드가 아닌 세트 기반 방식으로 수행됩니다.


12

REVERSE()방법은 즉 문자열의 절반 반전, "개선"

SELECT CASE WHEN RIGHT(@string, LEN(@string)/2) 
                 = REVERSE(LEFT(@string, LEN(@string)/2)) 
            THEN 1 ELSE 0 END AS Palindrome;

문자열에 홀수의 문자가 있으면 이상한 일이 일어날 것으로 기대하지 않습니다. 중간 문자를 확인할 필요가 없습니다.


@hvd는 모든 데이터 정렬에서 대리 쌍을 올바르게 처리하지 못할 수 있다고 언급했습니다.

@srutzky REVERSE()는 현재 데이터베이스의 기본 데이터 정렬이로 끝나는 경우에만 제대로 작동한다는 점에서 보조 문자 / 대리 쌍을 방법과 동일한 방식으로 처리한다고 설명했습니다 _SC.


8

을 사용하지 않고 REVERSE바로 생각해야하지만 여전히 함수 1을 사용합니다 . 나는 다음과 같은 것을 만들 것이다.

이 부분은 기존 기능이 이미 존재하는 경우 간단히 제거했습니다.

IF OBJECT_ID('dbo.IsPalindrome') IS NOT NULL
DROP FUNCTION dbo.IsPalindrome;
GO

이것은 함수 자체입니다.

CREATE FUNCTION dbo.IsPalindrome
(
    @Word NVARCHAR(500)
) 
RETURNS BIT
AS
BEGIN
    DECLARE @IsPalindrome BIT;
    DECLARE @LeftChunk NVARCHAR(250);
    DECLARE @RightChunk NVARCHAR(250);
    DECLARE @StrLen INT;
    DECLARE @Pos INT;

    SET @RightChunk = '';
    SET @IsPalindrome = 0;
    SET @StrLen = LEN(@Word) / 2;
    IF @StrLen % 2 = 1 SET @StrLen = @StrLen - 1;
    SET @Pos = LEN(@Word);
    SET @LeftChunk = LEFT(@Word, @StrLen);

    WHILE @Pos > (LEN(@Word) - @StrLen)
    BEGIN
        SET @RightChunk = @RightChunk + SUBSTRING(@Word, @Pos, 1)
        SET @Pos = @Pos - 1;
    END

    IF @LeftChunk = @RightChunk SET @IsPalindrome = 1;
    RETURN (@IsPalindrome);
END
GO

여기에서 함수를 테스트합니다.

IF dbo.IsPalindrome('This is a word') = 1 
    PRINT 'YES'
ELSE
    PRINT 'NO';

IF dbo.IsPalindrome('tattarrattat') = 1 
    PRINT 'YES'
ELSE
    PRINT 'NO';

이것은 단어의 전반부를 단어의 마지막 절반과 반대로 비교합니다 ( REVERSE함수 를 사용하지 않음 ). 이 코드는 홀수 및 짝수 단어를 올바르게 처리합니다. 단어 전체를 반복하는 대신 단어의 전반을 반복 LEFT한 다음 단어의 마지막 절반을 반복하여 오른쪽 절반의 반전 부분을 얻습니다. 단어가 홀수 길이이면 중간 문자를 건너 뜁니다. 정의에 따라 두 "반쪽"이 동일하기 때문입니다.


1-기능이 매우 느릴 수 있습니다!


6

REVERSE를 사용하지 않고는 ... 재귀 솔루션을 사용하는 것이 항상 재미 있습니다.)

create function dbo.IsPalindrome (@s varchar(max)) returns bit
as
begin
    return case when left(@s,1) = right(@s,1) then case when len(@s) < 3 then 1 else dbo.IsPalindrome(substring(@s,2,len(@s)-2)) end else 0 end;
end;
GO

select dbo.IsPalindrome('a')
1
select dbo.IsPalindrome('ab')
0
select dbo.IsPalindrome('bab')
1
select dbo.IsPalindrome('gohangasalamiimalasagnahog')
1

6

이것은 인라인 TVF 친화적 인 Martin Smith의 세트 기반 솔루션 버전 으로, 몇 가지 불필요한 개선 사항으로 추가로 장식되었습니다.

WITH Nums AS
(
  SELECT
    N = number
  FROM
    dbo.Numbers WITH(FORCESEEK) /*Requires a suitably indexed numbers table*/
)
SELECT
  IsPalindrome =
    CASE
      WHEN EXISTS
      (
        SELECT *
        FROM Nums
        WHERE N <= L / 2
          AND SUBSTRING(S, N, 1) <> SUBSTRING(S, 1 + L - N, 1)
      )
      THEN 0
      ELSE 1
    END
FROM
  (SELECT LTRIM(RTRIM(@Candidate)), LEN(@Candidate)) AS v (S, L)
;

5

In-Memory OLTP 기능을 갖춘 SQL Server 2016 스칼라 사용자 정의 함수는 다음과 같습니다.

ALTER FUNCTION dbo.IsPalindrome2 ( @inputString NVARCHAR(500) )
RETURNS BIT
WITH NATIVE_COMPILATION, SCHEMABINDING
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'English')

    DECLARE @i INT = 1, @j INT = LEN(@inputString)

    WHILE @i < @j
    BEGIN
        IF SUBSTRING( @inputString, @i, 1 ) != SUBSTRING( @inputString, @j, 1 )
        BEGIN
            RETURN(0)
        END
        ELSE
            SELECT @i+=1, @j-=1

    END

    RETURN(1)

END
GO

4

가장 큰 문제는 1보다 큰 값을 사용 LEFT하거나 RIGHT해당 위치의 문자가 아닌 여러 문자를 반환한다는 것입니다. 이 테스트 방법을 유지하려면 수정하는 간단한 방법은 다음과 같습니다.

RIGHT(LEFT(String,@n),1)=LEFT(RIGHT(String, @StringLength),1)

이것은 항상 왼쪽 문자열의 가장 오른쪽 문자와 오른쪽 문자열의 가장 왼쪽 문자를 가져옵니다.

아마도 이것을 확인하는 덜 회전하는 방법은 다음을 사용하는 것입니다 SUBSTRING.

SUBSTRING(String, @n, 1) = SUBSTRING(String, ((LEN(String) - @n) + 1), 1)

SUBSTRING이며, 따라서,도 1은 인덱스 + 1에서 ((LEN(String) - @n) + 1).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.