Uji apakah string adalah palindrom menggunakan T-SQL

24

Saya seorang pemula di T-SQL. Saya ingin memutuskan apakah string input adalah palindrome, dengan output = 0 jika tidak dan output = 1 jika itu. Saya masih mencari tahu sintaksisnya. Saya bahkan tidak mendapatkan pesan kesalahan. Saya mencari solusi yang berbeda dan beberapa umpan balik, untuk mendapatkan pemahaman dan pengetahuan yang lebih baik tentang bagaimana T-SQL bekerja, untuk menjadi lebih baik - Saya masih seorang mahasiswa.

Gagasan utama, seperti yang saya lihat, adalah untuk membandingkan karakter paling kiri dan paling kanan satu sama lain, untuk memeriksa kesetaraan, kemudian melanjutkan untuk membandingkan karakter kedua dari kiri dengan yang kedua dari yang terakhir, dll. Kami melakukan loop: Jika karakter sama satu sama lain, kami melanjutkan. Jika kita mencapai akhir, kita menghasilkan 1, jika tidak, kita menghasilkan 0.

Tolong kritik.

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

Saya pikir saya berada di jalur yang benar, tetapi saya masih jauh. Ada ide?

MSIS
sumber
LTRIM(RTRIM(...))ruang putih?
WernerCD

Jawaban:

60

Jika Anda menggunakan SQL Server, Anda dapat menggunakan fungsi REVERSE () untuk memeriksa?

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

Termasuk komentar Martin Smith, jika Anda menggunakan SQL Server 2012+ Anda dapat menggunakan fungsi IIF () :

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

Karena ada sejumlah solusi yang adil saya akan pergi dengan bagian "kritik" dari pertanyaan Anda. Beberapa catatan: Saya telah memperbaiki beberapa kesalahan ketik dan mencatat di mana saya melakukannya. Jika saya salah tentang mereka yang salah ketik sebutkan di komentar dan saya akan menjelaskan apa yang terjadi. Saya akan menunjukkan beberapa hal yang mungkin sudah Anda ketahui, jadi jangan tersinggung jika saya tahu. Beberapa komentar mungkin terlihat pilih-pilih tetapi saya tidak tahu di mana Anda berada dalam perjalanan Anda sehingga harus menganggap Anda baru memulai.

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

SELALU termasuk panjang dengan a charatau varchardefinisi. Aaron Bertrand berbicara tentang secara mendalam di sini . Dia berbicara tentang varchartetapi yang sama berlaku untuk char. Saya akan menggunakan varchar(255)untuk ini jika Anda hanya menginginkan string yang relatif pendek atau mungkin varchar(8000)untuk yang lebih besar atau bahkan varchar(max). Varcharadalah untuk string panjang variabel charhanya untuk yang tetap. Karena Anda tidak yakin panjang tali yang digunakan varchar. Juga binarytidak bin.

Selanjutnya Anda tidak perlu menempatkan semua variabel tersebut sebagai parameter. Nyatakan dalam kode Anda. Hanya masukkan sesuatu ke dalam daftar parameter jika Anda berencana meneruskannya atau keluar. (Anda akan melihat bagaimana ini terlihat di akhir.) Anda juga memiliki @StringLeftLength tetapi tidak pernah menggunakannya. Jadi saya tidak akan mendeklarasikannya.

Hal berikutnya yang akan saya lakukan adalah memformat ulang sedikit untuk membuat beberapa hal menjadi jelas.

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

Jika Anda melihat cara saya melakukan indentasi, Anda akan melihat bahwa saya memilikinya:

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

Itu karena perintah suka WHILEdan IFhanya memengaruhi baris kode pertama setelahnya. Anda harus menggunakan BEGIN .. ENDblok jika Anda menginginkan banyak perintah. Jadi memperbaiki yang kita dapatkan:

    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

Anda akan melihat bahwa saya hanya menambahkan BEGIN .. ENDblok di IF. Itu karena meskipun IFpernyataan itu panjang baris ganda (dan bahkan berisi banyak perintah) itu masih merupakan pernyataan tunggal (mencakup semua yang dilakukan di dalam IFdan ELSEbagian - bagian dari pernyataan).

Selanjutnya Anda akan mendapatkan kesalahan setelah Anda berdua RETURNs. Anda dapat mengembalikan variabel ATAU literal. Anda tidak dapat mengatur variabel dan mengembalikannya secara bersamaan.

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

    RETURN @Palindrome

Sekarang kita masuk ke dalam logika. Pertama-tama izinkan saya menunjukkan bahwa fungsi LEFTdan RIGHTfungsi yang Anda gunakan hebat, tetapi mereka akan memberi Anda jumlah karakter yang Anda lewati dari arah yang diminta. Jadi katakanlah Anda lulus dalam kata "test". Pada pass pertama Anda akan mendapatkan ini (menghapus variabel):

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

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

Jelas itu bukan yang Anda harapkan. Anda benar-benar ingin menggunakannya substring. Substring memungkinkan Anda melewati tidak hanya titik awal tetapi juga panjang. Jadi, Anda akan mendapatkan:

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

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

Selanjutnya Anda menambah variabel yang Anda gunakan dalam loop Anda hanya dalam satu kondisi pernyataan IF. Tarik variabel yang bertambah keluar dari struktur itu sepenuhnya. Itu akan membutuhkan BEGIN .. ENDblok tambahan , tapi saya bisa menghapus yang lain.

        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

Anda perlu mengubah WHILEkondisi Anda untuk memungkinkan tes terakhir.

        WHILE @StringLength > @n 

Dan last but not least, cara berdiri sekarang kita tidak menguji karakter terakhir jika ada jumlah karakter ganjil. Misalnya dengan 'ana' yang ntidak diuji. Itu baik-baik saja tetapi tidak saya perlu memperhitungkan satu kata huruf (jika Anda ingin itu dihitung sebagai positif itu adalah). Jadi kita bisa melakukannya dengan mengatur nilai di depan.

Dan sekarang kita akhirnya memiliki:

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

Satu komentar terakhir. Saya penggemar berat pemformatan secara umum. Ini benar-benar dapat membantu Anda melihat bagaimana kode Anda bekerja dan membantu menunjukkan kemungkinan kesalahan.

Edit

Seperti yang Sphinxxx sebutkan, kita masih memiliki kekurangan dalam logika kita. Setelah kita menekan ELSEdan mengatur @Palindromeke 0 tidak ada gunanya melanjutkan. Padahal pada titik itu kita bisa saja RETURN.

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

Mengingat bahwa kita sekarang hanya menggunakan @Palindromeuntuk "masih mungkin ini adalah palindrom" tidak ada gunanya memilikinya. Kita dapat menyingkirkan variabel dan mengalihkan logika kita ke hubungan pendek pada kegagalan (the RETURN 0) dan RETURN 1(respons positif) hanya jika itu membuat semua jalan melalui loop. Anda akan melihat ini sebenarnya menyederhanakan logika kami.

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
Kenneth Fisher
sumber
15

Anda juga bisa menggunakan pendekatan tabel Angka.

Jika Anda belum memiliki tabel angka bantu Anda dapat membuatnya sebagai berikut. Ini diisi dengan satu juta baris sehingga akan bagus untuk panjang string hingga 2 juta karakter.

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 

Di bawah ini membandingkan setiap karakter di sebelah kiri dengan mitra terkait di sebelah kanan, dan jika ada perbedaan ditemukan dapat korsleting dan mengembalikan 0. Jika string adalah panjang ganjil, karakter tengah tidak diperiksa karena ini tidak akan mengubah hasilnya .

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 

Jika Anda tidak yakin cara kerjanya, Anda dapat melihat dari bawah

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; 

masukkan deskripsi gambar di sini

Ini pada dasarnya adalah algoritma yang sama seperti yang dijelaskan dalam pertanyaan, tetapi dilakukan dengan cara yang ditetapkan berdasarkan daripada kode prosedur iteratif.

Martin Smith
sumber
12

The REVERSE()Metode "membaik", yaitu membalikkan hanya setengah dari string:

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

Saya tidak mengharapkan sesuatu yang aneh terjadi jika string memiliki jumlah karakter yang aneh; karakter tengah tidak harus diperiksa.


Sebuah komentar dikemukakan oleh @hvd bahwa ini mungkin tidak menangani pasangan pengganti dengan benar di semua pemeriksaan.

@srutzky berkomentar bahwa ia menangani Karakter Tambahan / Pasangan Pengganti dengan cara yang sama dengan REVERSE()metode ini, karena mereka hanya berfungsi dengan baik ketika Collation default basis data saat ini berakhir _SC.

ypercubeᵀᴹ
sumber
8

Tanpa menggunakan REVERSE, itulah yang langsung terlintas dalam pikiran, tetapi masih menggunakan fungsi 1 ; Saya akan membangun sesuatu seperti berikut ini.

Bagian ini hanya menghapus fungsi yang ada, jika sudah ada:

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

Ini fungsinya sendiri:

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

Di sini, kami menguji fungsinya:

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

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

Ini membandingkan paruh pertama kata dengan kebalikan dari setengah kata terakhir (tanpa menggunakan REVERSEfungsi). Kode ini menangani dengan benar kata-kata panjang dan ganjil. Alih-alih mengulang seluruh kata, kita cukup mendapatkan bagian LEFTdari kata pertama, kemudian mengulangi bagian terakhir kata untuk mendapatkan bagian yang dibalik dari bagian kanan. Jika kata itu panjangnya ganjil, kita lewati huruf tengah, karena menurut definisi itu akan sama untuk kedua "belahan".


1 - fungsi bisa sangat lambat!

Max Vernon
sumber
6

Tanpa menggunakan REVERSE ... Itu selalu menyenangkan untuk menggunakan solusi rekursif;) (Saya punya saya di SQL Server 2012, versi sebelumnya mungkin memiliki batasan pada rekursi)

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
Kevin Suchlicki
sumber
6

Ini adalah versi TVF-friendly inline dari solusi berbasis set Martin Smith , juga dihiasi dengan beberapa perangkat tambahan yang berlebihan:

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)
;
Andriy M
sumber
5

Hanya untuk bersenang-senang, inilah fungsi yang Ditentukan Pengguna SQL Server 2016 dengan Fitur OLTP Di Memori:

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
wBob
sumber
4

Masalah utama yang akan Anda hadapi adalah bahwa dengan nilai lebih dari 1, LEFTatau RIGHTakan mengembalikan banyak karakter, bukan karakter pada posisi itu. Jika Anda ingin tetap menggunakan metode pengujian ini, cara yang sangat sederhana untuk mengubahnya adalah

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

Ini akan selalu mengambil karakter paling kanan dari string kiri dan karakter paling kiri dari string kanan.

Mungkin cara yang kurang bundaran untuk memeriksa ini, adalah dengan menggunakan SUBSTRING:

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

Perhatikan bahwa SUBSTRING1-diindeks, maka + 1di ((LEN(String) - @n) + 1).

Andy
sumber