The biggest difference is when the finalizer/destructor is called. With RAII, it is always called when the variable goes out of scope, it has a set time with certain guarantees. With garbage collection, it is called at any time after the system determines the object is no longer in use.
Conceptually, garbage collection is an implementation detail of objects with infinite lifetime. As far as your program is concerned, a GC'd object is always around. That's why it isn't collected until there's no more references to it, so the program never needs to know it was collected. Finalizers are a slight hole in this fiction, but they are still subject to some rules: you don't know when they will be called. You don't even know /if/ they will be called.
You can see the difference side-by-side in D. A struct destructor in D gives you RAII. A class destructor works with garbage collection. (Mostly, you can tweak these too, they aren't hard rules.)
Class destructors:
Cannot reference members unless they are value types in the class. The GC might have already collected them because by the time the destructor runs, the class is /already/ considered dead.
Won't run until an allocation is requested under memory pressure, triggering a GC collection sweep. It may never run at all.
It's a substitute for RAII only in special circumstances. RAII's guarantees let you use it with scarce resources.